http://www.cnblogs.com/3me-linux/p/6122444.html
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.樣本
在這裡,寫了一個簡單的通知鏈表的代碼。
實際上,整個通知鏈的編寫也就兩個過程:
首先是定義自己的通知鏈的前端節點,並將要執行的函數註冊到自己的通知鏈中。
其次則是由另外的子系統來通知這個鏈,讓其上面註冊的函數運行。
我這裡將第一個過程分成了兩步來寫,第一步是定義了前端節點和一些自訂的註冊函數(針對該前端節點的),第二步則是使用自訂的註冊函數註冊了一些通知鏈節點。分別在代碼buildchain.c與regchain.c中。
發送通知資訊的代碼為notify.c。
代碼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"); /* * 定義自己的通知鏈頭結點以及註冊和卸載通知鏈的外包函數 */ /* * 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通知鏈中發送訊息,讓鏈中的函數運行。 #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) {