前言
在linux内核中,各个子系统之间有很强的相互关系,某些子系统可能对其他子系统产生的事件比较感兴趣。因此内核引入了notifier机制,当然了notifier机制只能用在内核子系统之间,不能用在内核与应用层之间。比如当系统suspend的时候,就会使用到notifier机制来通知系统的内核线程进行suspend。
内核实现的notifier机制代码位于kernel/kernel/notifier.c,同时此机制的代码量也不是很多只有600行左右。
数据结构
内核使用struct notifier_block结构代表一个notifier
- typedef int (*notifier_fn_t)(struct notifier_block *nb,
- unsigned long action, void *data);
- struct notifier_block {
- notifier_fn_t notifier_call;
- struct notifier_block __rcu *next;
- int priority;
- };
next: 用来链接同一个类型的notifier。
priority: notifier chain的优先级。对应的数字越大优先级越高,就优先执行。
同时内核也提供了四种不同类型的notifier chain
- 原子通知链(Atomic notifier chains)
- struct atomic_notifier_head {
- spinlock_t lock;
- struct notifier_block __rcu *head;
- };
很明显因为atomic_notifer_head其中的spin_lock的特点就是不能睡眠。
- 可阻塞通知链(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;
- };
- SRCU通知链(SRCU notifier chains)
- struct srcu_notifier_head {
- struct mutex mutex;
- struct srcu_struct srcu;
- struct notifier_block __rcu *head;
- };
notifier chain初始化
内核提供了一套宏用来初始化各个类型的通知链
- #define ATOMIC_INIT_NOTIFIER_HEAD(name) do { \
- spin_lock_init(&(name)->lock); \
- (name)->head = NULL; \
- } while (0)
- #define BLOCKING_INIT_NOTIFIER_HEAD(name) do { \
- init_rwsem(&(name)->rwsem); \
- (name)->head = NULL; \
- } while (0)
- #define RAW_INIT_NOTIFIER_HEAD(name) do { \
- (name)->head = NULL; \
- } while (0)
- #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)
- void srcu_init_notifier_head(struct srcu_notifier_head *nh)
- {
- mutex_init(&nh->mutex);
- if (init_srcu_struct(&nh->srcu) < 0)
- BUG();
- nh->head = NULL;
- }
注册/注销通知链
内核提供的最基本的注册通知链的函数
- /*
- * 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;
- }
因为插入/删除操作都是临界资源,需要使用rcu机制保护起来。
同样,内核通过包装核心的注册/注销函数,实现了上述说的四种notifier chain
- int atomic_notifier_chain_register(struct atomic_notifier_head *nh,struct notifier_block *n)
- int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,struct notifier_block *n)
- int blocking_notifier_chain_register(struct blocking_notifier_head *nh,struct notifier_block *n)
- int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,struct notifier_block *n)
- int raw_notifier_chain_register(struct raw_notifier_head *nh,struct notifier_block *n)
- int raw_notifier_chain_unregister(struct raw_notifier_head *nh,struct notifier_block *n)
- int srcu_notifier_chain_register(struct srcu_notifier_head *nh,struct notifier_block *n).
- int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,struct notifier_block *n)
通知函数
当某种事件需要发生的时候,就需要调用内核提供的通知函数notifier call函数,来通知注册过相应时间的子系统。
- /**
- * 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 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) //有停止的mask就返回,否则继续
- break;
- nb = next_nb;
- nr_to_call--;
- }
- return ret;
- }
- 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)
示例分析
通过编写两个文件,一个用来注册事件,另一个用来通知事件。
notifier.c用来注册事件
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/notifier.h>
- BLOCKING_NOTIFIER_HEAD(test_chain_head);
- EXPORT_SYMBOL_GPL(test_chain_head);
- int register_test_notifier(struct notifier_block *nb)
- {
- return blocking_notifier_chain_register(&test_chain_head, nb);
- }
- int unregister_test_notifier(struct notifier_block *nb)
- {
- return blocking_notifier_chain_unregister(&test_chain_head, nb);
- }
- static int test_chain_notify(struct notifier_block *nb,unsigned long mode, void *_unused)
- {
- printk(KERN_EMERG "notifier: test_chain_notify!\n"); //回调处理函数
- return 0;
- }
- static struct notifier_block test_chain_nb = {
- .notifier_call = test_chain_notify,
- };
- static int notifier_test_init(void)
- {
- printk(KERN_EMERG "notifier: notifier_test_init!\n");
- register_test_notifier(&test_chain_nb); //注册notifier事件
- return 0;
- }
- static void notifier_test_exit(void)
- {
- printk(KERN_EMERG "notifier: notifier_test_exit!\n");
- unregister_test_notifier(&test_chain_nb);
- }
- module_init(notifier_test_init);
- module_exit(notifier_test_exit);
- MODULE_LICENSE("GPL v2");
call.c用来触发事件。
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/notifier.h>
- extern struct blocking_notifier_head test_chain_head;
- static int call_notifier_call_chain(unsigned long val)
- {
- int ret = blocking_notifier_call_chain(&test_chain_head, val, NULL);
- return notifier_to_errno(ret);
- }
- static int call_test_init(void)
- {
- printk(KERN_EMERG "notifier: call_test_init!\n");
- call_notifier_call_chain(123); //在init函数中触发事件
- return 0;
- }
- static void call_test_exit(void)
- {
- printk(KERN_EMERG "notifier: call_test_exit!\n");
- }
- module_init(call_test_init);
- module_exit(call_test_exit);
- MODULE_LICENSE("GPL v2");
- root@test:/data # insmod notifier.ko
- root@test:/data # insmod call.ko
- root@test:/data # dmesg | grep "notifier"
- [ 89.644596] c7 notifier: notifier_test_init!
- [ 95.956801] c6 notifier: call_test_init!
- [ 95.960624] c6 notifier: test_chain_notify!