首頁 > 軟體

Linux核心通知鏈(notifier chain)

2020-06-16 17:26:42

概述

Linux核心中各個子系統相互依賴,當其中某個子系統狀態發生改變時,就必須使用一定的機制告知使用其服務的其他子系統,以便其他子系統採取相應的措施。為滿足這樣的需求,核心實現了事件通知鏈機制(notification chain)。

通知鏈只能用在各個子系統之間,而不能在核心和使用者空間進行事件的通知。組成核心的核心系統程式碼均位於kernel目錄下,通知連結串列位於kernel/notifier.c中,對應的標頭檔案為include/linux/notifier.h。

事件通知連結串列是一個事件處理常式的列表,每個通知鏈都與某個或某些事件有關,當特定的事件發生時,就呼叫相應的事件通知鏈中的回撥函數,進行相應的處理。

通知鏈型別

原子通知鏈
 通知鏈元素的回撥函數(當事件發生時要執行的函數)在中斷或原子操作上下文中執行,不允許阻塞。對應的連結串列頭結構:

struct atomic_notifier_head { 
    spinlock_t  lock; 
    struct  notifier_block *head; 
};

可阻塞通知鏈
 通知鏈元素的回撥函數在進程上下文中執行,允許阻塞。對應的連結串列頭:

struct  blocking_notifier_head { 
    struct  rw_semaphore  rwsem; 
    struct  notifier_block  *head; 
};

原始通知鏈
 對通知鏈元素的回撥函數沒有任何限制,所有鎖和保護機制都由呼叫者維護。對應的連結串列頭:

struct  raw_notifier_head { 
    struct  notifier_block  *head; 
};

SRCU通知鏈
 可阻塞通知鏈的一種變體。對應的連結串列頭:

struct  srcu_notifier_head { 
    struct  mutex mutex; 
    struct  srcu_struct  srcu; 
    struct  notifier_block  *head; 
};

相應介面

註冊通知鏈
 在通知連結串列註冊時,需要有一個連結串列頭,他指向這個通知連結串列的第一個元素,這樣就可以根據這個連結串列頭找到這個連結串列中的所有資料。

static int notifier_chain_register(struct notifier_block **nl,
                struct notifier_block *n);
static int notifier_chain_unregister(struct notifier_block **nl,
                struct notifier_block *n)

通知連結串列
當有事件發生時,就使用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)

範例

#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>

static RAW_NOTIFIER_HEAD(test_chain_head);
#define EVENT_A 0x01
#define EVENT_B 0x02

int test_notifier_event(struct notifier_block *nb, unsigned long event, void *v)
{
    switch (event) {
    case EVENT_A:
        printk("test_notifier_event EVENT_An");
        break;
    case EVENT_B:
        printk("test_notifier_event EVENT_Bn");
        break;
    default:
        break;
    }

    return NOTIFY_DONE;
}

static struct notifier_block test_notifier = {
    .notifier_call = test_notifier_event,
};

static int __init mynotify_init(void)
{
    printk("raw_notifier_chain_registern");
    raw_notifier_chain_register(&test_chain_head, &test_notifier);

    printk("raw_notifier_call_chainn");
    raw_notifier_call_chain(&test_chain_head, EVENT_B, NULL);
    raw_notifier_call_chain(&test_chain_head, EVENT_A, NULL);

    return 0;
}

static void __exit mynotify_exit(void)
{
    raw_notifier_chain_unregister(&test_chain_head, &test_notifier);
}

module_init(mynotify_init);
module_exit(mynotify_exit);

MODULE_AUTHOR("lei_wang");
MODULE_LICENSE("GPL");

首先要將notifier_block通過raw_notifier_chain_register註冊到notifier_block_head連結串列上,notifier_block中必須要實現回撥函數,然後在需要的時候通過raw_notifier_call_chain去呼叫notifier_block中的回撥函數。

 


IT145.com E-mail:sddin#qq.com