前言
在linux内核中,各个子系统之间有很强的相互关系,某些子系统可能对其他子系统产生的事件比较感兴趣。因此内核引入了notifier机制,当然了notifier机制只能用在内核子系统之间,不能用在内核与应用层之间。比如当系统suspend的时候,就会使用到notifier机制来通知系统的内核线程进行suspend。
内核实现的notifier机制代码位于kernel/kernel/notifier.c,同时此机制的代码量也不是很多只有600行左右。
数据结构
内核使用struct notifier_block结构代表一个notifier
[cpp] view plain copy
    
  
- typedef int (*notifier_fn_t)(struct notifier_block *nb,
 - long action, void *data);
 - struct notifier_block {
 - notifier_fn_t notifier_call;
 - struct notifier_block __rcu *next;
 - int priority;
 - };
 
notifier_call:  代表当事件发生之后调用的回调函数。
next: 用来链接同一个类型的notifier。
priority: notifier chain的优先级。对应的数字越大优先级越高,就优先执行。
同时内核也提供了四种不同类型的notifier chain
- 原子通知链(Atomic notifier chains)
 
[cpp] view plain copy
     
   
- struct atomic_notifier_head {
 - spinlock_t lock;
 - struct notifier_block __rcu *head;
 - };
 
可以看到原子notifier chain只是对notifier_block的一个封装。同时atomic notifier chain的回调函数需要运行在中断上下文/原子上下文中,而且不能睡眠。
 
 
很明显因为atomic_notifer_head其中的spin_lock的特点就是不能睡眠。
 
- 可阻塞通知链(Blocking notifier chains)
 
[cpp] view plain copy
      
    
- struct blocking_notifier_head {
 - struct rw_semaphore rwsem;
 - struct notifier_block __rcu *head;
 - };
 
blocking_notifier_head其中包含了读写信号量成员rwsem,而信号量的特定就是运行在进程上下文,而且还可以睡眠。同理Blocking notifier chains的回调函数特征一样。
  
  
- 原始通知链(Raw notifier chains)
 
[cpp] view plain copy
       
     
- struct raw_notifier_head {
 - struct notifier_block __rcu *head;
 - };
 
raw_notifier_head的特点是对回调函数,register, unregister都没有任何限制,所有的保护机制都需要调用者维护。
   
   
- SRCU通知链(SRCU notifier chains)
 
   
[cpp] view plain copy
      
    
- struct srcu_notifier_head {
 - struct mutex mutex;
 - struct srcu_struct srcu;
 - struct notifier_block __rcu *head;
 - };
 
SRCU通知链是block notifier chain的一种变体,采用SRCU(Sleepable Read-Copy Update)代替rw-semphore来保护chains
  
  
  
notifier chain初始化
 
内核提供了一套宏用来初始化各个类型的通知链
 
[cpp] view plain copy
     
   
- #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)
 
以上是动态初始化各个类型的通知链,当然了有动态初始化,也就有静态初始化
 
 
[cpp] view plain copy
     
   
- #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)
 
通过注释可以知道SRCU通知链不能使用静态的方法,因此内核提供了一个动态的初始化函数,
 
 
[cpp] view plain copy
     
   
- 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;
 - }
 
  
注册/注销通知链
  
内核提供的最基本的注册通知链的函数
  
[cpp] view plain copy
      
    
- /*
 - * 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;
 - }
 
上述的操作就是通过判断priority的大小,然后将大的插入带链表头,小的插入在链表末尾。
  
  
[cpp] view plain copy
      
    
- 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
  
[cpp] view plain copy
      
    
- 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函数,来通知注册过相应时间的子系统。
   
[cpp] view plain copy
       
     
- /**
 - * 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,
 - 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);
 - //调用注册的回调函数
 - if (nr_calls)
 - (*nr_calls)++;
 - if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) //有停止的mask就返回,否则继续
 - break;
 - nb = next_nb;
 - nr_to_call--;
 - }
 - return ret;
 - }
 
同样内核也提供了四个不同类型的通知函数
   
   
[cpp] view plain copy
       
     
- 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用来注册事件
    
[cpp] view plain copy
        
      
- #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)
 - {
 - "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)
 - {
 - "notifier: notifier_test_init!\n");
 - //注册notifier事件
 - return 0;
 - }
 - static void notifier_test_exit(void)
 - {
 - "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用来触发事件。
    
    
[cpp] view plain copy
        
      
- #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)
 - {
 - "notifier: call_test_init!\n");
 - //在init函数中触发事件
 - return 0;
 - }
 - static void call_test_exit(void)
 - {
 - "notifier: call_test_exit!\n");
 - }
 - module_init(call_test_init);
 - module_exit(call_test_exit);
 - MODULE_LICENSE("GPL v2");
 
测试结构如下:
    
    
[cpp] view plain copy
        
      
- 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!
 










