最近在看《深入理解Linux网络内幕》一书,学习了一下书中讲到的内核通知链方面的知识,写了一个读书笔记和一点代码来加深理解,希望能够对大家有一点帮助。
1.通知链表简介
大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子 系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。 通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知 方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。其实和系统调 用signal的思想差不多。 2.通知链表数据结构
通知链表的节点类型为notifier_block,其定义如下: struct notifier_block { int (*notifier_call)(struct notifier_block *self, unsigned long, void *); struct notifier_block *next; int priority; }; |
其中最重要的就是notifier_call这个函数指针,表示了这个节点所对应的要运行的那个函数。next指向下一个节点,即当前事件发生时还要继续执行的那些节点。
3.注册通知链
在通知链注册时,需要有一个链表头,它指向这个通知链表的第一个元素。这样,之后的事件对该链表通知时就会根据这个链表头而找到这个链表中所有的元素。 注册的函数是: int notifier_chain_register(struct notifier_block **nl, struct notifier_block *n) 也即是将新的节点n加入到nl所指向的链表中去。 卸载的函数是: int notifier_chain_unregister(strut notifier_block **nl, struct notifier_block *n) 也即是将节点n从nl所指向的链表中删除。 4.通知链表
当有事件发生时,就使用notifier_call_chain向某个通知链表发送消息。 int notifier_call_chain(struct notifier_block **nl, unsigned long val, void *v) 这个函数是按顺序运行nl指向的链表上的所有节点上注册的函数。简单地说,如下所示: struct notifier_block *nb = *n; while (nb) { ret = nb->notifier_call(nb, val, v); if (ret & NOTIFY_STOP_MASK) { return ret; } nb = nb->next; } |
5.示例
在这里,写了一个简单的通知链表的代码。 代码1 buildchain.c
它的作用是自定义一个通知链表test_chain,然后再自定义两个函数分别向这个通知链中加入或删除节点,最后再定义一个函数通知这个test_chain链。
#include <asm/uaccess.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/notifier.h> #include <linux/init.h> #include <linux/types.h> #include <linux/module.h> MODULE_LICENSE("GPL"); static RAW_NOTIFIER_HEAD(test_chain); int register_test_notifier(struct notifier_block *nb) { return raw_notifier_chain_register(&test_chain, nb); } EXPORT_SYMBOL(register_test_notifier); int unregister_test_notifier(struct notifier_block *nb) { return raw_notifier_chain_unregister(&test_chain, nb); } EXPORT_SYMBOL(unregister_test_notifier); int test_notifier_call_chain(unsigned long val, void *v) { return raw_notifier_call_chain(&test_chain, val, v); } EXPORT_SYMBOL(test_notifier_call_chain); static int __init init_notifier(void) { printk("init_notifier\n"); return 0; } static void __exit exit_notifier(void) { printk("exit_notifier\n"); } module_init(init_notifier); module_exit(exit_notifier); |
代码2 regchain.c
该代码的作用是将test_notifier1 test_notifier2 test_notifier3这三个节点加到之前定义的test_chain这个通知链表上,同时每个节点都注册了一个函数。
#include <asm/uaccess.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/notifier.h> #include <linux/init.h> #include <linux/types.h> #include <linux/module.h> MODULE_LICENSE("GPL"); extern int register_test_notifier(struct notifier_block*); extern int unregister_test_notifier(struct notifier_block*); static int test_event1(struct notifier_block *this, unsigned long event, void *ptr) { printk("In Event 1: Event Number is %d\n", event); return 0; } static int test_event2(struct notifier_block *this, unsigned long event, void *ptr) { printk("In Event 2: Event Number is %d\n", event); return 0; } static int test_event3(struct notifier_block *this, unsigned long event, void *ptr) { printk("In Event 3: Event Number is %d\n", event); return 0; } static struct notifier_block test_notifier1 = { .notifier_call = test_event1, }; static struct notifier_block test_notifier2 = { .notifier_call = test_event2, }; static struct notifier_block test_notifier3 = { .notifier_call = test_event3, }; static int __init reg_notifier(void) { int err; printk("Begin to register:\n"); err = register_test_notifier(&test_notifier1); if (err) { printk("register test_notifier1 error\n"); return -1; } printk("register test_notifier1 completed\n"); err = register_test_notifier(&test_notifier2); if (err) { printk("register test_notifier2 error\n"); return -1; } printk("register test_notifier2 completed\n"); register_test_notifier(&test_notifier3); if (err) |