Linux內核通知鏈notifier

 

Linux內核通知鏈notifier

 


1.內核通知鏈表簡介(引用網絡資料)
    大多數內核子系統都是相互獨立的,因此某個子系統可能對其它子系統產生的事件感興趣。爲了滿足這個需求,也即是讓某個子系統在發生某個事件時通知其它的子系統,Linux內核提供了通知鏈的機制。通知鏈表只能夠在內核的子系統之間使用,而不能夠在內核與用戶空間之間進行事件的通知。
   
通知鏈表是一個函數鏈表,鏈表上的每一個節點都註冊了一個函數。當某個事情發生時,鏈表上所有節點對應的函數就會被執行。所以對於通知鏈表來說有一個通知方與一個接收方。在通知這個事件時所運行的函數由被通知方決定,實際上也即是被通知方註冊了某個函數,在發生某個事件時這些函數就得到執行。其實和系統調用signal的思想差不多。

通知鏈技術可以概括爲:事件的被通知者將事件發生時應該執行的操作通過函數指針方式保存在鏈表(通知鏈)中,然後當事件發生時通知者依次執行鏈表中每一個元素的回調函數完成通知。

2.內核通知鏈表數據結構
    通知鏈表的節點類型爲notifier_block,其定義如下:

struct notifier_block {

int (*notifier_call)(struct notifier_block *, unsigned long, void *);


struct notifier_block *next;


int priority;

};

notifier_call:該節點所對應的要運行的函數。
*next:指向下一個節點,事件發生時,依次執行的

3.內核通知鏈註冊函數:
    在通知鏈註冊時,需要有一個鏈表頭,它指向這個通知鏈表的第一個元素。這樣,之後的事件對該鏈表通知時就會根據這個鏈表頭而找到這個鏈表中所有的元素。鏈表頭的定義見本文第6節。
    註冊的函數是:

/*

*
Notifier chain core routines.
The exported routines below


*
are layered on top of these, with appropriate locking added.


*/


static int notifier_chain_register(struct notifier_block **nl,

struct notifier_block *n)

{

while ((*nl) != NULL) {


if (n->priority > (*nl)->priority)


break;


nl = &((*nl)->next);


}


n->next = *nl;


rcu_assign_pointer(*nl, n);


return 0;

}

   從上面的函數實現來看,被通知者調用 notifier_chain_register 函數註冊回調函數,該函數是按照優先級將回調函數加入到通知鏈中去的。


    卸載的函數是:
static int notifier_chain_unregister(struct notifier_block **nl,

struct notifier_block *n)

{

while ((*nl) != NULL) {


if ((*nl) == n) {


rcu_assign_pointer(*nl, n->next);


return 0;


}


nl = &((*nl)->next);


}


return -ENOENT;

}

將節點nnl所指向的鏈表中刪除。
kernel/notifier.c中內核根據通知鏈的類型分別包裝了上面這個函數:

int atomic_notifier_chain_register(struct atomic_notifier_head *nh,

struct notifier_block *n)


int blocking_notifier_chain_register(struct blocking_notifier_head *nh,

struct notifier_block *n)


int raw_notifier_chain_register(struct raw_notifier_head *nh,

struct notifier_block *n)


int srcu_notifier_chain_register(struct srcu_notifier_head *nh,

struct notifier_block *n)



4.內核通知鏈通知函數:
    當有事件發生時,通知者調用 notifier_call_chain 函數通知事件的到達,這個函數會遍歷n1指向的通知鏈中所有的元素,然後依次調用每一個的回調函數,完成通知動作。


static int __kprobes notifier_call_chain(struct notifier_block **nl,

unsigned long val, void *v,


int nr_to_call,
int *nr_calls)

{

int ret = NOTIFY_DONE;


struct notifier_block *nb, *next_nb;



nb = rcu_dereference_raw(*nl);



while (nb && nr_to_call) {


next_nb = rcu_dereference_raw(nb->next);


#ifdef CONFIG_DEBUG_NOTIFIERS

if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {


WARN(1, "Invalid notifier called!");


nb = next_nb;


continue;


}

#endif

ret = nb->notifier_call(nb, val, v);



if (nr_calls)


(*nr_calls)++;



if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)


break;


nb = next_nb;


nr_to_call--;


}


return ret;

}

kernel/notifier.c中內核根據通知鏈的類型分別包裝了上面這個函數:

int atomic_notifier_call_chain(struct atomic_notifier_head *nh,

unsigned long val, void *v)


int blocking_notifier_call_chain(struct blocking_notifier_head *nh,

unsigned long val, void *v)


int raw_notifier_call_chain(struct raw_notifier_head *nh,

unsigned long val, void *v)


int srcu_notifier_call_chain(struct srcu_notifier_head *nh,

unsigned long val, void *v)


5.通知鏈四種類型

5.1)原子通知鏈的鏈頭
通知鏈元素的回調函數(當事件發生時要執行的函數)只能在中斷上下文中運行,不允許阻塞。
struct atomic_notifier_head {

spinlock_t lock;


struct notifier_block *head;

};
5.2)可阻塞通知鏈:
通知鏈元素的回調函數在進程上下文中運行,允許阻塞。
struct blocking_notifier_head {

struct rw_semaphore rwsem;


struct notifier_block *head;

};
5.3)原始通知鏈:
對通知鏈元素的回調函數沒有任何限制,所有鎖和保護機制都由調用者維護。
struct raw_notifier_head {

struct notifier_block *head;

};
5.4SRCU 通知鏈:
可阻塞通知鏈的變種。
struct srcu_notifier_head {

struct mutex mutex;


struct srcu_struct srcu;


struct notifier_block *head;

};

6)定義一個通知鏈的頭部結點並初始化:
include/linux/Notifier.h
初始化宏定義:


#define ATOMIC_NOTIFIER_INIT(name) {
\


.lock = __SPIN_LOCK_UNLOCKED(name.lock),
\


.head = NULL }

#define BLOCKING_NOTIFIER_INIT(name) {
\


.rwsem = __RWSEM_INITIALIZER((name).rwsem),
\


.head = NULL }

#define RAW_NOTIFIER_INIT(name)
{
\


.head = NULL }

/* srcu_notifier_heads cannot be initialized statically */

定義通知鏈:

#define ATOMIC_NOTIFIER_HEAD(name)
\


struct atomic_notifier_head name =
\


ATOMIC_NOTIFIER_INIT(name)

#define BLOCKING_NOTIFIER_HEAD(name)
\


struct blocking_notifier_head name =
\


BLOCKING_NOTIFIER_INIT(name)

#define RAW_NOTIFIER_HEAD(name)
\


struct raw_notifier_head name =
\


RAW_NOTIFIER_INIT(name)


發佈了0 篇原創文章 · 獲贊 2 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章