Linux內(nèi)核基礎(chǔ) | 通知鏈機(jī)制
一、通知鏈簡介
舉個(gè)形象的例子:將通知鏈比喻成”訂閱者-發(fā)布者“,訂閱者將感興趣的公眾號關(guān)注并設(shè)置提醒,發(fā)布者一旦發(fā)布某個(gè)文章,訂閱者即可收到通知看到發(fā)布的內(nèi)容。
在Linux內(nèi)核中為了及時(shí)響應(yīng)某些到來的事件,采取了通知鏈機(jī)制。該機(jī)制的兩個(gè)角色的任務(wù):
1、通知者定義通知鏈
2、被通知者向通知鏈中注冊回調(diào)函數(shù)
3、當(dāng)事件發(fā)生時(shí),通知者發(fā)送通知 (執(zhí)行通知鏈上每個(gè)調(diào)用塊上的回調(diào)函數(shù))所以通知鏈?zhǔn)且粋€(gè)單鏈表,單鏈表上的節(jié)點(diǎn)是調(diào)用塊,每個(gè)調(diào)用塊上有事件相關(guān)的回調(diào)函數(shù)和調(diào)用塊的優(yōu)先級。當(dāng)事件觸發(fā)時(shí)會(huì)按優(yōu)先級順序執(zhí)行該鏈表上的回調(diào)函數(shù)。通知鏈只用于各個(gè)子系統(tǒng)之間,不能用于內(nèi)核和用戶空間進(jìn)行事件的通知。
二、相關(guān)細(xì)節(jié)
1、通知鏈的類型
原子通知鏈( Atomic notifier chains ):
通知鏈元素的回調(diào)函數(shù)(當(dāng)事件發(fā)生時(shí)要執(zhí)行的函數(shù))只能在中斷上下文中運(yùn)行,不允許阻塞。
可阻塞通知鏈( Blocking notifier chains ):
通知鏈元素的回調(diào)函數(shù)在進(jìn)程上下文中運(yùn)行,允許阻塞。
原始通知鏈( Raw notifier chains ):
對通知鏈元素的回調(diào)函數(shù)沒有任何限制,所有鎖和保護(hù)機(jī)制都由調(diào)用者維護(hù)。
SRCU 通知鏈( SRCU notifier chains ):可阻塞通知鏈的一種變體
本文將以原子通知鏈進(jìn)行分析
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ? ??


2、原子通知鏈與通知塊
初始化一個(gè)原子通知鏈?zhǔn)褂靡韵潞甓x
例如創(chuàng)建一個(gè)設(shè)備通知鏈隊(duì)列頭:
struct raw_notifier_head就相當(dāng)于存放這條通知鏈單鏈表頭,每一個(gè)通知鏈上的元素也就是通知塊如下定義:
回調(diào)函數(shù)接口:
整個(gè)通知鏈的組織如下圖所示:

3、向通知鏈中插入通知塊
4、調(diào)用通知鏈
三、編寫內(nèi)核模塊進(jìn)行實(shí)驗(yàn)
1、案例1
編寫內(nèi)核模塊作為被通知者,向內(nèi)核netdev_chain通知鏈中插入自定義通知塊(在通知塊中自定義事件觸發(fā)的回調(diào)函數(shù)),源碼如下:
Makefile:
將模塊插入內(nèi)核后,將網(wǎng)卡關(guān)閉再重啟一次,查看日志信息:
2、案例2
通過寫兩個(gè)內(nèi)核模塊,其中一個(gè)作為通知者一個(gè)作為被通知者
module_1.c:
初始化一個(gè)通知鏈
定義事件的回調(diào)函數(shù)并向通知鏈中插入三個(gè)通知塊(與之前定義的回調(diào)函數(shù)相對應(yīng))
測試通知鏈:循環(huán)遍歷通知鏈的通知塊,并同時(shí)調(diào)用對應(yīng)的回調(diào)函數(shù)
module_2.c:模擬某事件發(fā)生,并調(diào)用通知鏈
(module_1與module_2的Makefile可參考上面的Demo1)
運(yùn)行時(shí)先插入module_1再插入module_2結(jié)果如下,紅框內(nèi)是module_1中的測試輸出日志,綠框內(nèi)為世界調(diào)用通知鏈時(shí)的執(zhí)行結(jié)果日志。

從上面可以看到通知鏈的執(zhí)行順序是按照優(yōu)先級進(jìn)行的,那么當(dāng)調(diào)用通知鏈時(shí)是否每個(gè)通知塊上的回調(diào)函數(shù)都會(huì)執(zhí)行呢?
答案:不是,每個(gè)被執(zhí)行的notifier_block回調(diào)函數(shù)的返回值可能取值以下幾個(gè):
NOTIFY_DONE:表示對相關(guān)的事件類型不關(guān)心。
NOTIFY_OK:順利執(zhí)行。
NOTIFY_BAD:執(zhí)行有錯(cuò)。
NOTIFY_STOP:停止執(zhí)行后面的回調(diào)函數(shù)。
NOTIFY_STOP_MASK:停止執(zhí)行的掩碼
如當(dāng)返回值NOTIFY_STOP_MASK會(huì)停止執(zhí)行后面優(yōu)先級低的調(diào)用塊的函數(shù)。
例如把module_1中通知塊的回調(diào)函數(shù)B_call的返回值修改為NOTIFY_STOP_MASK后,重新編譯,運(yùn)行結(jié)果如下,只執(zhí)行了調(diào)用鏈中調(diào)用塊2的回調(diào)函數(shù)。

