Linux Notifier Chains

來源:互聯網
上載者:User

轉自:http://hi.baidu.com/zmdesperado/blog/item/a8d701eec86ffcc2b31cb1fe.html
Linux Notifier Chains
1.    引言
Linux是單核心架構(monolithic kernel),大多數核心子系統和模組是相互獨立的,它們被動態地載入或卸載,以使核心變得小巧和可擴充。然而,子系統或模組之間需要通訊,或者說某個特定模組撲捉到的事件可能其它模組對此感興趣,這就需要一種機制來滿足子系統或模組之間互動的需求。
Linux使用通知鏈表來實現這一需求,它是一個簡單的函數鏈表,當某件事件發生時,鏈表上的函數就會執行。這是一種發布-訂閱(publish-subscribe)模式,當客戶(訂閱者)需要某個特定事件的通知時,會向主機(發行者)註冊自己;接下來,只要感興趣的事件一發生,主機便會通知客戶。
2.    Notifier定義
在/include/linux/notifier.h檔案中定義了通知鏈表節點
struct notifier_block {
    int (*notifier_call)(struct notifier_block *, unsigned long, void *);
    struct notifier_block *next;
    int priority;
};
其中,函數指標notifier_call註冊了當某個事件發生時需要調用的函數;next指向下一個鏈表節點;priority設定鏈表節點的優先順序,數值越大優先順序越高,預設為0。因此,所有的通知鏈表節點群組成了一個單鏈表,並以優先順序(priority)排列。
3.    Notifier類型
核心提供了四種類型的通知鏈表,它們的分類是基於執行內容和調用通知鏈所需的鎖保護機制:
·Atomic notifier chains:該通知鏈表在中斷或原子上下文執行,不能阻塞,鏈表事件對回應時間要求高,定義如下
    struct atomic_notifier_head {
        spinlock_t lock;
        struct notifier_block *head;
};
·Blocking notifier chains:在進程上下文執行,能夠阻塞,鏈表事件對回應時間要求不高,定義如下
    struct blocking_notifier_head {
        struct rw_semaphore rwsem;
        struct notifier_block *head;
};
·Raw notifier chains:調用,註冊或卸載鏈表通知時無限制,所需保護機制由調用者提供,定義如下
    struct raw_notifier_head {
        struct notifier_block *head;
};
·SRCU notifier chains:這是一種Sleepable Read Copy Update (SRCU)的鏈表通知,與block鏈表通知類似,不同在處理鎖與保護上,SRCU在調用通知時的系統開銷小,而從通知鏈表中去除通知調用的系統開銷大,因此適合用在調用通知頻繁,而移除調用通知少的情況中,定義如下
struct srcu_notifier_head {
    struct mutex mutex;
    struct srcu_struct srcu;
    struct notifier_block *head;
};
4.    Notifier註冊
以blocking notifier chains為例,通常用一個宏來定義並初始化一個通知鏈表頭,代碼如下
#define BLOCKING_NOTIFIER_HEAD(name)                \
    struct blocking_notifier_head name =            \
        BLOCKING_NOTIFIER_INIT(name)
然後向通知鏈表中註冊通知節點,代碼如下
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
        struct notifier_block *n)
{
    int ret;

    /*
     * This code gets used during boot-up, when task switching is
     * not yet working and interrupts must remain disabled.  At
     * such times we must not call down_write().
     */
    if (unlikely(system_state == SYSTEM_BOOTING))
        return notifier_chain_register(&nh->head, n);

    down_write(&nh->rwsem);
    ret = notifier_chain_register(&nh->head, n);
    up_write(&nh->rwsem);
    return ret;
}
第一個參數為通知鏈表頭指標,而第二參數是要註冊到該通知鏈表中的鏈表節點,調用函數notifier_chain_register( )實現了真正的註冊,代碼如下
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;
}
當blocking notifier chains的頭指標head為NULL時,將所要註冊的notifier block
賦給head,而該鏈表節點的next設為NULL;當blocking notifier chains的頭指標head為非空時,若所要註冊的notifier block的優先順序比前端節點的高,則將該鏈表節點的next指向前端節點,而將該鏈表節點作為新的前端節點;當blocking notifier chains的頭指標head為非空時,且所要註冊的notifier block的優先順序比前端節點的低,則將依據優先順序高低遍曆該通知鏈表上的節點,找到鏈表上第一個比自身優先順序低的節點,將所要註冊的鏈表節點插入到該節點之前。
5.    Notifier發送
仍以blocking notifier chains為例,當一個通知鏈表上註冊了鏈表節點後,需要一個函數去按優先順序去啟用鏈表上註冊的函數,代碼如下
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
        unsigned long val, void *v)
{
    return __blocking_notifier_call_chain(nh, val, v, -1, NULL);
}

int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
                   unsigned long val, void *v,
                   int nr_to_call, int *nr_calls)
{
    int ret = NOTIFY_DONE;

    /*
     * We check the head outside the lock, but if this access is
     * racy then it does not matter what the result of the test
     * is, we re-check the list after having taken the lock anyway:
     */
    if (rcu_dereference(nh->head)) {
        down_read(&nh->rwsem);
        ret = notifier_call_chain(&nh->head, val, v, nr_to_call,
                    nr_calls);
        up_read(&nh->rwsem);
    }
    return ret;
}
我們發現調用的底層函數是notifier_call_chain( ),代碼如下
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(*nl);

    while (nb && nr_to_call) {
        next_nb = rcu_dereference(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;
}
第一個參數傳入的是所要通知的鏈表頭指標,第二個和第三個參數是要傳遞給通知函數的整型和指標參數,第四個參數nr_to_call限定了通知鏈表上響應的函數個數,為-1時無限制,第五個參數nr_call記錄了實際調用的通知鏈表上的函數個數,為NULL時不記錄。
6.    Notifier執行個體應用
以framebuffer子系統為例,簡單介紹通知鏈表的實現。
在/drivers/video/fb_notify.c中初始化了一個名為fb_notifier_list的通知鏈表頭,代碼如下
static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
在/drivers/video/console/fbcon.c檔案中,函數fb_console_init( )在初始化framebuffer控制台時調用了函數fb_register_client(&fbcon_event_notifier)向通知鏈表頭fb_notifier_list註冊了一個名為fbcon_event_notifier的通知節點,定義如下
static struct notifier_block fbcon_event_notifier = {
    .notifier_call    = fbcon_event_notify,
};
其中fbcon_event_notify就是通知回呼函數;而fb_register_client最終調用的就是註冊通知節點的底層函數notifier_chain_register( )。
在/drivers/video/fbmem.c檔案中,函數register_framebuffer( )在初始化註冊framebuffer驅動時,在所有初始化工作完成後調用了函數fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event)給通知鏈表fb_notifier_list發送事件FB_EVENT_FB_REGISTERED,表明framebuffer登入成功了,該函數最終調用了底層的鏈表通知函數notifier_call_chain( ),此時將遍曆通知鏈表上所用的通知節點,顯然節點fbcon_event_notifier的通知函數fbcon_event_notify將最終被調用執行

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.