1.通知鏈表簡介
大多數內核子系統都是相互獨立的,所以某個子系統可能對其它子系統產生的事件感興趣。爲了知足這個需求,也便是讓某個子系統在發生某個事件時通知其它的子系統,Linux內核提供了通知鏈的機制。通知鏈表只可以在內核的子系統之間使用,而不可以在內核與用戶空間之間進行事件的通知。
通知鏈表是一個函數鏈表,鏈表上的每個節點都註冊了一個函數。當某個事情發生時,鏈表上全部節點對應的函數就會被執行。因此對於通知鏈表來講有一個通知方與一個接收方。在通知這個事件時所運行的函數由被通知方決定,實際上也便是被通知方註冊了某個函數,在發生某個事件時這些函數就獲得執行。其實和系統調用signal的思想差很少。
linux
2.通知鏈表數據結構
通知鏈表的節點類型爲notifier_block,其定義以下:shell
- 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指向的鏈表上的全部節點上註冊的函數。簡單地說,以下所示:ui
- 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.示例
在這裏,寫了一個簡單的通知鏈表的代碼。
實際上,整個通知鏈的編寫也就兩個過程:
首先是定義本身的通知鏈的頭節點,並將要執行的函數註冊到本身的通知鏈中。
其次則是由另外的子系統來通知這個鏈,讓其上面註冊的函數運行。
我這裏將第一個過程分紅了兩步來寫,第一步是定義了頭節點和一些自定義的註冊函數(針對該頭節點的),第二步則是使用自定義的註冊函數註冊了一些通知鏈節點。分別在代碼buildchain.c與regchain.c中。
發送通知信息的代碼爲notify.c。
代碼1 buildchain.c
它的做用是自定義一個通知鏈表test_chain,而後再自定義兩個函數分別向這個通知鏈中加入或刪除節點,最後再定義一個函數通知這個test_chain鏈。
this
- #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");
- /*
- * 定義本身的通知鏈頭結點以及註冊和卸載通知鏈的外包函數
- */
- /*
- * RAW_NOTIFIER_HEAD是定義一個通知鏈的頭部結點,
- * 經過這個頭部結點能夠找到這個鏈中的其它全部的notifier_block
- */
- static RAW_NOTIFIER_HEAD(test_chain);
- /*
- * 自定義的註冊函數,將notifier_block節點加到剛剛定義的test_chain這個鏈表中來
- * raw_notifier_chain_register會調用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);
- /*
- * 自定義的通知鏈表的函數,即通知test_chain指向的鏈表中的全部節點執行相應的函數
- */
- 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);
複製代碼
代碼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;
- }
- /*
- * 事件1,該節點執行的函數爲test_event1
- */
- static struct notifier_block test_notifier1 =
- {
- .notifier_call = test_event1,
- };
- /*
- * 事件2,該節點執行的函數爲test_event1
- */
- static struct notifier_block test_notifier2 =
- {
- .notifier_call = test_event2,
- };
- /*
- * 事件3,該節點執行的函數爲test_event1
- */
- 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");
- 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);
複製代碼
代碼3 notify.c
該代碼的做用就是向test_chain通知鏈中發送消息,讓鏈中的函數運行。code
- #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);
- /*
- * 向通知鏈發送消息以觸發註冊了的函數
- */
- static int __init call_notifier(void)
- {
- int err;
- printk("Begin to notify:\n");
- /*
- * 調用自定義的函數,向test_chain鏈發送消息
- */
- 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);
複製代碼
Makefile文件事件
- obj-m:=buildchain.o regchain.o notify.o
- KERNELDIR:=/lib/modules/$(shell uname -r)/build
- default:
- make -C $(KERNELDIR) M=$(shell pwd) modules
複製代碼
運行:it
- make
- insmod buildchain.ko
- insmod regchain.ko
- insmod notify.ko
複製代碼
這樣就能夠看到通知鏈運行的效果了
下面是我在本身的機器上面運行獲得的結果:
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 ==============================