Linux内核的通知链机制

原文:http://blog.csdn.net/npy_lp/article/details/7415380

内核源码:linux-2.6.38.8.tar.bz2

 

    在Linux内核中,通知链是一种非常好的异步通信机制,它的实现也非常简单,就是通过某个单循环链表来实现。

    1、通知链实例都使用notifier_block结构体来表示 

  1. /* linux-2.6.38.8/include/linux/notifier.h */  
  2. struct notifier_block {  
  3.     int (*notifier_call)(struct notifier_block *, unsigned longvoid *);//处理函数  
  4.     struct notifier_block __rcu *next; //用于构建单循环链表  
  5.     int priority; //为本链表中的通知链实例设定优先级,高优先级的处理函数将优先被执行  
  6. };  

    2、在当前的Linux内核中有四种类型的通知链链表表头(增加了某种锁机制而已) 

  1. /* linux-2.6.38.8/include/linux/notifier.h */  
  2. struct atomic_notifier_head {  
  3.     spinlock_t lock;  
  4.     struct notifier_block __rcu *head;  
  5. };  
  6.   
  7. struct blocking_notifier_head {  
  8.     struct rw_semaphore rwsem;  
  9.     struct notifier_block __rcu *head;  
  10. };  
  11.   
  12. struct raw_notifier_head {  
  13.     struct notifier_block __rcu *head;  
  14. };  
  15.   
  16. struct srcu_notifier_head {  
  17.     struct mutex mutex;  
  18.     struct srcu_struct srcu;  
  19.     struct notifier_block __rcu *head;  
  20. };  

    静态地创建链表表头(所谓静态地创建就是定义一个相应的结构体变量,而动态地创建是使用指向结构体变量的指针),srcu_notifier_head类型的表头不支持静态地创建。 

  1. /* linux-2.6.38.8/include/linux/notifier.h */  
  2. #define ATOMIC_NOTIFIER_HEAD(name)              \  
  3.     struct atomic_notifier_head name =          \  
  4.         ATOMIC_NOTIFIER_INIT(name)  
  5. #define BLOCKING_NOTIFIER_HEAD(name)                \  
  6.     struct blocking_notifier_head name =            \  
  7.         BLOCKING_NOTIFIER_INIT(name)  
  8. #define RAW_NOTIFIER_HEAD(name)                 \  
  9.     struct raw_notifier_head name =             \  
  10.         RAW_NOTIFIER_INIT(name)  
  11.   
  12.   
  13. #define ATOMIC_NOTIFIER_INIT(name) {                \  
  14.         .lock = __SPIN_LOCK_UNLOCKED(name.lock),    \  
  15.         .head = NULL }  
  16. #define BLOCKING_NOTIFIER_INIT(name) {              \  
  17.         .rwsem = __RWSEM_INITIALIZER((name).rwsem), \  
  18.         .head = NULL }  
  19. #define RAW_NOTIFIER_INIT(name) {               \  
  20.         .head = NULL }  

    3、注册和注销(实际上就是向相应的链表中插入或删除通知链实例) 

  1. /* linux-2.6.38.8/include/linux/notifier.h */  
  2. extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,  
  3.         struct notifier_block *nb);  
  4. extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,  
  5.         struct notifier_block *nb);  
  6. extern int raw_notifier_chain_register(struct raw_notifier_head *nh,  
  7.         struct notifier_block *nb);  
  8. extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,  
  9.         struct notifier_block *nb);  
  10.   
  11. extern int blocking_notifier_chain_cond_register(  
  12.         struct blocking_notifier_head *nh,  
  13.         struct notifier_block *nb);  
  14.   
  15. extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,  
  16.         struct notifier_block *nb);  
  17. extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,  
  18.         struct notifier_block *nb);  
  19. extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,  
  20.         struct notifier_block *nb);  
  21. extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,  
  22.         struct notifier_block *nb);  

    它们最后都会调用notifier_chain_register、notifier_chain_cond_register(只被blocking_notifier_chain_cond_register函数所使用)或notifier_chain_unregister函数。 

  1. /* linux-2.6.38.8/kernel/notifier.c */  
  2. static int notifier_chain_register(struct notifier_block **nl,  
  3.         struct notifier_block *n)  
  4. {  
  5.     while ((*nl) != NULL) { //检查链表是否不为空。  
  6.         if (n->priority > (*nl)->priority) //检查将要插入的通知链实例的priority是否大于找到的链表元素的priority值,priority值最大的被插入到表头后面  
  7.             break;  
  8.         nl = &((*nl)->next); //指向链表的下一个元素  
  9.     }  
  10.     n->next = *nl;  
  11.     rcu_assign_pointer(*nl, n); //*nl指向n  
  12.     return 0;  
  13. }  
  14.   
  15. static int notifier_chain_cond_register(struct notifier_block **nl,  
  16.         struct notifier_block *n)  
  17. {  
  18.     while ((*nl) != NULL) {  
  19.         if ((*nl) == n)  //检查被插入的通知链实例是否已经存在,避免重复插入  
  20.             return 0;  
  21.         if (n->priority > (*nl)->priority)  
  22.             break;  
  23.         nl = &((*nl)->next);  
  24.     }  
  25.     n->next = *nl;  
  26.     rcu_assign_pointer(*nl, n);  
  27.     return 0;  
  28. }  
  29.   
  30. static int notifier_chain_unregister(struct notifier_block **nl,  
  31.         struct notifier_block *n)  
  32. {  
  33.     while ((*nl) != NULL) {  
  34.         if ((*nl) == n) {  
  35.             rcu_assign_pointer(*nl, n->next);  
  36.             return 0;  
  37.         }  
  38.         nl = &((*nl)->next);  
  39.     }  
  40.     return -ENOENT;  
  41. }  

  1. /* linux-2.6.38.8/include/linux/rcupdate.h */  
  2. #define rcu_assign_pointer(p, v) \  
  3.     __rcu_assign_pointer((p), (v), __rcu)  
  4.   
  5. #define __rcu_assign_pointer(p, v, space) \  
  6.     ({ \  
  7.         if (!__builtin_constant_p(v) || \  
  8.             ((v) != NULL)) \  
  9.             smp_wmb(); \  
  10.         (p) = (typeof(*v) __force space *)(v); \  
  11.     })  

    4、触发相应的处理函数 (即使用链表中的通知链实例)

  1. /* linux-2.6.38.8/include/linux/notifier.h */  
  2. extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,  
  3.         unsigned long val, void *v);  
  4. extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,  
  5.     unsigned long val, void *v, int nr_to_call, int *nr_calls);  
  6. extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,  
  7.         unsigned long val, void *v);  
  8. extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,  
  9.     unsigned long val, void *v, int nr_to_call, int *nr_calls);  
  10. extern int raw_notifier_call_chain(struct raw_notifier_head *nh,  
  11.         unsigned long val, void *v);  
  12. extern int __raw_notifier_call_chain(struct raw_notifier_head *nh,  
  13.     unsigned long val, void *v, int nr_to_call, int *nr_calls);  
  14. extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,  
  15.         unsigned long val, void *v);  
  16. extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,  
  17.     unsigned long val, void *v, int nr_to_call, int *nr_calls);  

    它们最后都会调用notifier_call_chain函数。 

  1. /* linux-2.6.38.8/kernel/notifier.c */  
  2. static int __kprobes notifier_call_chain(struct notifier_block **nl,  
  3.                     unsigned long val, void *v,  
  4.                     int nr_to_call, int *nr_calls)  
  5. {  
  6.     int ret = NOTIFY_DONE;  
  7.     struct notifier_block *nb, *next_nb;  
  8.   
  9.     nb = rcu_dereference_raw(*nl);  
  10.   
  11.     while (nb && nr_to_call) {  //根据需要,会调用链表中nr_to_call个通知链实例的处理函数,优先级最高的将被最先调用  
  12.         next_nb = rcu_dereference_raw(nb->next);  
  13.   
  14. #ifdef CONFIG_DEBUG_NOTIFIERS  
  15.         if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {  
  16.             WARN(1, "Invalid notifier called!");  
  17.             nb = next_nb;  
  18.             continue;  
  19.         }  
  20. #endif  
  21.         ret = nb->notifier_call(nb, val, v); //调用通知链实例的处理函数  
  22.   
  23.         if (nr_calls)  
  24.             (*nr_calls)++;  
  25.   
  26.         if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)  
  27.             break;  
  28.         nb = next_nb;  
  29.         nr_to_call--;  
  30.     }  
  31.     return ret;  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值