2021-05-12 14:32:11
Linux核心通知鏈(notifier chain)
概述
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中的回撥函數。
相關文章