Linux內核通知鏈notifier

Linux內核通知鏈notifier

源地址:http://fangjian0518.blog.163.com/blog/static/5591965620118295401816/

1.內核通知鏈表簡介(引用網絡資料)

    大多數內核子系統都是相互獨立的,因此某個子系統可能對其它子系統產生的事件感興趣。爲了滿足這個需求,也即是讓某個子系統在發生某個事件時通知其它的子系統,Linux內核提供了通知鏈的機制。通知鏈表只能夠在內核的子系統之間使用,而不能夠在內核與用戶空間之間進行事件的通知。

通知鏈表是一個函數鏈表,鏈表上的每一個節點都註冊了一個函數。當某個事情發生時,鏈表上所有節點對應的函數就會被執行。所以對於通知鏈表來說有一個通知方與一個接收方。在通知這個事件時所運行的函數由被通知方決定,實際上也即是被通知方註冊了某個函數,在發生某個事件時這些函數就得到執行。


通知鏈技術可以概括爲:事件的被通知者將事件發生時應該執行的操作通過函數指針方式保存在鏈表(通知鏈)中,然後當事件發生時通知者依次執行鏈表中每一個元素的回調函數完成通知。(回調函數)
The notifier chain facility is a general mechanism provided by the kernel. It is designed to provide a way for kernel elements to express interest in being informed about the occurrence of general asynchronous events.

The basic building block of the mechanism is the struct notifier_block which is defined in include/linux/notifier.h.

struct notifier_block {
        int (*notifier_call)(struct notifier_block *, unsigned long, void *); 
        struct notifier_block __rcu *next;
        int priority; /*用於對註冊者進行優先級排隊,高優先級的處理例程將被優先執行,由註冊者自己指定 */
};


The block contains a pointer to the function to be called when the event occurs. The parameters passed to the notifier function include:

? a pointer to the notifier block itself,
? an event code such as NETDEV_REGISTER or NETDEV_UNREGISTER,
? and a pointer to an unspecified private data type which in the case of the network chain points to the associated struct netdevice.

The kernel function notifier_chain_register() assembles related notifier blocks into notifier chains. Modules within the networking subsystem use the register_netdevice_notifier() function defined in net/core/dev.c to add their own notifier blocks to the netdev_chain which is statically initialized as NULL in dev.c.

int register_netdevice_notifier(struct notifier_block *nb)
{
    return notifier_chain_register(&netdev_chain, nb);
}


Adding the notifier_block to the chain.

The kernel routine notifier_chain_register() links the notifier block into the specified chain in priority order.

/*
 * 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;
}


卸載函數是:

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;
}


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

/**
 * notifier_call_chain - Informs the registered notifiers about an event.
 * @nl: Pointer to head of the blocking notifier chain
 * @val: Value passed unmodified to notifier function
 * @v: Pointer passed unmodified to notifier function
 * @nr_to_call: Number of notifier functions to be called. Don't care
 * value of this parameter is -1.
 * @nr_calls: Records the number of notifications sent. Don't care
 * value of this field is NULL.
 * @returns: notifier_call_chain returns the value returned by the
 * last notifier function called.
 */

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);


                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;
}

參數nl是通知鏈的頭部,val表示事件類型,v用來指向通知鏈上的函數執行時需要用到的參數,一般不同的通知鏈,參數類型也不一樣,例如當通知一個網卡被註冊時,v就指向net_device結構,nr_to_call表示準備最多通知幾個,-1表示整條鏈都通知,nr_calls非空的話,返回通知了多少個。

    每個被執行的notifier_block回調函數的返回值可能取值爲以下幾個:

  1. NOTIFY_DONE:表示對相關的事件類型不關心。
  2. NOTIFY_OK:順利執行。
  3. NOTIFY_BAD:執行有錯。
  4. NOTIFY_STOP:停止執行後面的回調函數。
  5. NOTIFY_STOP_MASK:停止執行的掩碼。

Notifier_call_chain()把最後一個被調用的回調函數的返回值作爲它的返回值。


內核預定義了四種類型的notifier chain.

/*
 * Notifier chains are of four types:
 *
 
* Atomic notifier chains: Chain callbacks run in interrupt/atomic context. Callouts are not allowed to block.


 * Blocking notifier chains: Chain callbacks run in process context. Callouts are allowed to block.


 * Raw notifier chains: There are no restrictions on callbacks, registration, or unregistration. All locking and protection must be provided by the caller.


 * SRCU notifier chains: A variant of blocking notifier chains, with the same restrictions.

 *
 * atomic_notifier_chain_register() may be called from an atomic context, but blocking_notifier_chain_register() and srcu_notifier_chain_register() must be called from a process context. Ditto for the corresponding _unregister() routines.
 *
 * atomic_notifier_chain_unregister(), blocking_notifier_chain_unregister(),and srcu_notifier_chain_unregister() _must not_ be called from within the call chain.
 *
 * SRCU notifier chains are an alternative form of blocking notifier chains. They use SRCU (Sleepable Read-Copy Update) instead of rw-semaphores for protection of the chain links. This means there is _very_ low overheadin srcu_notifier_call_chain(): no cache bounces and no memory barriers. As compensation, srcu_notifier_chain_unregister() is rather expensive. SRCU notifier chains should be used when the chain will be called very often but notifier_blocks will seldom be removed. Also, SRCU notifier chains are slightly more difficult to use because they require special runtime initialization.

 */

/* 原子通知鏈( Atomic notifier chains ):通知鏈元素的回調函數(當事件發生時要執行的函數)只能在中斷上下文中運行,不允許阻塞 */
struct atomic_notifier_head {
        spinlock_t lock;
        struct notifier_block __rcu *head;
};

/* 可阻塞通知鏈( Blocking notifier chains ):通知鏈元素的回調函數在進程上下文中運行,允許阻塞 */
struct blocking_notifier_head {
        struct rw_semaphore rwsem;
        struct notifier_block __rcu *head;
};

/* 原始通知鏈( Raw notifier chains ):對通知鏈元素的回調函數沒有任何限制,所有鎖和保護機制都由調用者維護 */
struct raw_notifier_head {
        struct notifier_block __rcu *head;
};

/* 可阻塞通知鏈的一種變體 */
struct srcu_notifier_head {
        struct mutex mutex;
        struct srcu_struct srcu;
        struct notifier_block __rcu *head;
};


對應四中通知鏈的註冊函數分別是:

extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh, structnotifier_block *nb);
extern int 
blocking_notifier_chain_register(struct blocking_notifier_head *nh, structnotifier_block *nb);
extern int raw_notifier_chain_register(struct raw_notifier_head *nh, structnotifier_block *nb);
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, structnotifier_block *nb);


卸載函數分別是:

extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, structnotifier_block *nb);
extern int 
blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, structnotifier_block *nb);
extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh, structnotifier_block *nb);
extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, structnotifier_block *nb);


通知函數分別是:

extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val,void *v);
extern int 
blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned longval, void *v);
extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, v);

extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val,void *v);oid *v);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章