- 論壇徽章:
- 0
|
最近在看《深入理解Linux網(wǎng)絡(luò)內(nèi)幕》一書,學(xué)習(xí)了一下書中講到的內(nèi)核通知鏈方面的知識,寫了一個(gè)讀書筆記和一點(diǎn)代碼來加深理解,希望能夠?qū)Υ蠹矣幸稽c(diǎn)幫助。內(nèi)核通知鏈在網(wǎng)絡(luò)方面得到了廣泛的使用。
1.通知鏈表簡介
大多數(shù)內(nèi)核子系統(tǒng)都是相互獨(dú)立的,因此某個(gè)子系統(tǒng)可能對其它子系統(tǒng)產(chǎn)生的事件感興趣。為了滿足這個(gè)需求,也即是讓某個(gè)子系統(tǒng)在發(fā)生某個(gè)事件時(shí)通知其它的子系統(tǒng),Linux內(nèi)核提供了通知鏈的機(jī)制。通知鏈表只能夠在內(nèi)核的子系統(tǒng)之間使用,而不能夠在內(nèi)核與用戶空間之間進(jìn)行事件的通知。
通知鏈表是一個(gè)函數(shù)鏈表,鏈表上的每一個(gè)節(jié)點(diǎn)都注冊了一個(gè)函數(shù)。當(dāng)某個(gè)事情發(fā)生時(shí),鏈表上所有節(jié)點(diǎn)對應(yīng)的函數(shù)就會被執(zhí)行。所以對于通知鏈表來說有一個(gè)通知方與一個(gè)接收方。在通知這個(gè)事件時(shí)所運(yùn)行的函數(shù)由被通知方?jīng)Q定,實(shí)際上也即是被通知方注冊了某個(gè)函數(shù),在發(fā)生某個(gè)事件時(shí)這些函數(shù)就得到執(zhí)行。其實(shí)和系統(tǒng)調(diào)用signal的思想差不多。
2.通知鏈表數(shù)據(jù)結(jié)構(gòu)
通知鏈表的節(jié)點(diǎn)類型為notifier_block,其定義如下:
- struct notifier_block
- {
- int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
- struct notifier_block *next;
- int priority;
- };
復(fù)制代碼
其中最重要的就是notifier_call這個(gè)函數(shù)指針,表示了這個(gè)節(jié)點(diǎn)所對應(yīng)的要運(yùn)行的那個(gè)函數(shù)。next指向下一個(gè)節(jié)點(diǎn),即當(dāng)前事件發(fā)生時(shí)還要繼續(xù)執(zhí)行的那些節(jié)點(diǎn)。
3.注冊通知鏈
在通知鏈注冊時(shí),需要有一個(gè)鏈表頭,它指向這個(gè)通知鏈表的第一個(gè)元素。這樣,之后的事件對該鏈表通知時(shí)就會根據(jù)這個(gè)鏈表頭而找到這個(gè)鏈表中所有的元素。
注冊的函數(shù)是:
int notifier_chain_register(struct notifier_block **nl, struct notifier_block *n)
也即是將新的節(jié)點(diǎn)n加入到nl所指向的鏈表中去。
卸載的函數(shù)是:
int notifier_chain_unregister(strut notifier_block **nl, struct notifier_block *n)
也即是將節(jié)點(diǎn)n從nl所指向的鏈表中刪除。
4.通知鏈表
當(dāng)有事件發(fā)生時(shí),就使用notifier_call_chain向某個(gè)通知鏈表發(fā)送消息。
int notifier_call_chain(struct notifier_block **nl, unsigned long val, void *v)
這個(gè)函數(shù)是按順序運(yùn)行nl指向的鏈表上的所有節(jié)點(diǎn)上注冊的函數(shù)。簡單地說,如下所示:
- struct notifier_block *nb = *n;
-
- while (nb)
- {
- ret = nb->notifier_call(nb, val, v);
- if (ret & NOTIFY_STOP_MASK)
- {
- return ret;
- }
- nb = nb->next;
- }
復(fù)制代碼
5.示例
在這里,寫了一個(gè)簡單的通知鏈表的代碼。
實(shí)際上,整個(gè)通知鏈的編寫也就兩個(gè)過程:
首先是定義自己的通知鏈的頭節(jié)點(diǎn),并將要執(zhí)行的函數(shù)注冊到自己的通知鏈中。
其次則是由另外的子系統(tǒng)來通知這個(gè)鏈,讓其上面注冊的函數(shù)運(yùn)行。
我這里將第一個(gè)過程分成了兩步來寫,第一步是定義了頭節(jié)點(diǎn)和一些自定義的注冊函數(shù)(針對該頭節(jié)點(diǎn)的),第二步則是使用自定義的注冊函數(shù)注冊了一些通知鏈節(jié)點(diǎn)。分別在代碼buildchain.c與regchain.c中。
發(fā)送通知信息的代碼為notify.c。
代碼1 buildchain.c
它的作用是自定義一個(gè)通知鏈表test_chain,然后再自定義兩個(gè)函數(shù)分別向這個(gè)通知鏈中加入或刪除節(jié)點(diǎn),最后再定義一個(gè)函數(shù)通知這個(gè)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");
- /*
- * 定義自己的通知鏈頭結(jié)點(diǎn)以及注冊和卸載通知鏈的外包函數(shù)
- */
-
- /*
- * RAW_NOTIFIER_HEAD是定義一個(gè)通知鏈的頭部結(jié)點(diǎn),
- * 通過這個(gè)頭部結(jié)點(diǎn)可以找到這個(gè)鏈中的其它所有的notifier_block
- */
-
- static RAW_NOTIFIER_HEAD(test_chain);
- /*
- * 自定義的注冊函數(shù),將notifier_block節(jié)點(diǎn)加到剛剛定義的test_chain這個(gè)鏈表中來
- * raw_notifier_chain_register會調(diào)用notifier_chain_register
- */
-
- 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);
- /*
- * 自定義的通知鏈表的函數(shù),即通知test_chain指向的鏈表中的所有節(jié)點(diǎn)執(zhí)行相應(yīng)的函數(shù)
- */
-
- 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);
- /*
- * init and exit
- */
-
- 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);
復(fù)制代碼
代碼2 regchain.c
該代碼的作用是將test_notifier1 test_notifier2 test_notifier3這三個(gè)節(jié)點(diǎn)加到之前定義的test_chain這個(gè)通知鏈表上,同時(shí)每個(gè)節(jié)點(diǎn)都注冊了一個(gè)函數(shù)。
- #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;
- }
- /*
- * 事件1,該節(jié)點(diǎn)執(zhí)行的函數(shù)為test_event1
- */
-
- static struct notifier_block test_notifier1 =
- {
- .notifier_call = test_event1,
- };
- /*
- * 事件2,該節(jié)點(diǎn)執(zhí)行的函數(shù)為test_event1
- */
-
- static struct notifier_block test_notifier2 =
- {
- .notifier_call = test_event2,
- };
- /*
- * 事件3,該節(jié)點(diǎn)執(zhí)行的函數(shù)為test_event1
- */
-
- static struct notifier_block test_notifier3 =
- {
- .notifier_call = test_event3,
- };
- /*
- * 對這些事件進(jìn)行注冊
- */
-
- 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");
- err = register_test_notifier(&test_notifier3);
- if (err)
- {
- printk("register test_notifier3 error\n");
- return -1;
- }
- printk("register test_notifier3 completed\n");
- return err;
- }
- /*
- * 卸載剛剛注冊了的通知鏈
- */
-
- static void __exit unreg_notifier(void)
- {
- printk("Begin to unregister\n");
- unregister_test_notifier(&test_notifier1);
- unregister_test_notifier(&test_notifier2);
- unregister_test_notifier(&test_notifier3);
- printk("Unregister finished\n");
- }
- module_init(reg_notifier);
- module_exit(unreg_notifier);
復(fù)制代碼
代碼3 notify.c
該代碼的作用就是向test_chain通知鏈中發(fā)送消息,讓鏈中的函數(shù)運(yùn)行。
- #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 test_notifier_call_chain(unsigned long val, void *v);
- /*
- * 向通知鏈發(fā)送消息以觸發(fā)注冊了的函數(shù)
- */
-
- static int __init call_notifier(void)
- {
- int err;
- printk("Begin to notify:\n");
- /*
- * 調(diào)用自定義的函數(shù),向test_chain鏈發(fā)送消息
- */
-
- printk("==============================\n");
- err = test_notifier_call_chain(1, NULL);
- printk("==============================\n");
- if (err)
- printk("notifier_call_chain error\n");
- return err;
- }
- static void __exit uncall_notifier(void)
- {
- printk("End notify\n");
- }
- module_init(call_notifier);
- module_exit(uncall_notifier);
復(fù)制代碼
Makefile文件
- obj-m:=buildchain.o regchain.o notify.o
- KERNELDIR:=/lib/modules/$(shell uname -r)/build
- default:
- make -C $(KERNELDIR) M=$(shell pwd) modules
復(fù)制代碼
運(yùn)行:
- make
- insmod buildchain.ko
- insmod regchain.ko
- insmod notify.ko
復(fù)制代碼
這樣就可以看到通知鏈運(yùn)行的效果了
下面是我在自己的機(jī)器上面運(yùn)行得到的結(jié)果:
init_notifier
Begin to register:
register test_notifier1 completed
register test_notifier2 completed
register test_notifier3 completed
Begin to notify:
==============================
In Event 1: Event Number is 1
In Event 2: Event Number is 1
In Event 3: Event Number is 1
==============================
[ 本帖最后由 scutan 于 2008-12-11 22:33 編輯 ] |
評分
-
查看全部評分
|