原文:http://blog.csdn.net/npy_lp/article/details/7415380
内核源码:linux-2.6.38.8.tar.bz2
在Linux内核中,通知链是一种非常好的异步通信机制,它的实现也非常简单,就是通过某个单循环链表来实现。
1、通知链实例都使用notifier_block结构体来表示
- /* linux-2.6.38.8/include/linux/notifier.h */
- struct notifier_block {
- int (*notifier_call)(struct notifier_block *, unsigned long, void *);//处理函数
- struct notifier_block __rcu *next; //用于构建单循环链表
- int priority; //为本链表中的通知链实例设定优先级,高优先级的处理函数将优先被执行
- };
2、在当前的Linux内核中有四种类型的通知链链表表头(增加了某种锁机制而已)
- /* linux-2.6.38.8/include/linux/notifier.h */
- struct atomic_notifier_head {
- spinlock_t lock;
- struct notifier_block __rcu *head;
- };
- struct blocking_notifier_head {
- struct rw_semaphore rwsem;
- struct notifier_block __rcu *head;
- };
- struct raw_notifier_head {
- struct notifier_block __rcu *head;
- };
- struct srcu_notifier_head {
- struct mutex mutex;
- struct srcu_struct srcu;
- struct notifier_block __rcu *head;
- };
静态地创建链表表头(所谓静态地创建就是定义一个相应的结构体变量,而动态地创建是使用指向结构体变量的指针),srcu_notifier_head类型的表头不支持静态地创建。
- /* linux-2.6.38.8/include/linux/notifier.h */
- #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)
- #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 }
3、注册和注销(实际上就是向相应的链表中插入或删除通知链实例)
- /* linux-2.6.38.8/include/linux/notifier.h */
- extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
- struct notifier_block *nb);
- extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
- struct notifier_block *nb);
- extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
- struct notifier_block *nb);
- extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
- struct notifier_block *nb);
- extern int blocking_notifier_chain_cond_register(
- struct blocking_notifier_head *nh,
- struct notifier_block *nb);
- extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
- struct notifier_block *nb);
- extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
- struct notifier_block *nb);
- extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
- struct notifier_block *nb);
- extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
- struct notifier_block *nb);
它们最后都会调用notifier_chain_register、notifier_chain_cond_register(只被blocking_notifier_chain_cond_register函数所使用)或notifier_chain_unregister函数。
- /* linux-2.6.38.8/kernel/notifier.c */
- static int notifier_chain_register(struct notifier_block **nl,
- struct notifier_block *n)
- {
- while ((*nl) != NULL) { //检查链表是否不为空。
- if (n->priority > (*nl)->priority) //检查将要插入的通知链实例的priority是否大于找到的链表元素的priority值,priority值最大的被插入到表头后面
- break;
- nl = &((*nl)->next); //指向链表的下一个元素
- }
- n->next = *nl;
- rcu_assign_pointer(*nl, n); //*nl指向n
- return 0;
- }
- static int notifier_chain_cond_register(struct notifier_block **nl,
- struct notifier_block *n)
- {
- while ((*nl) != NULL) {
- if ((*nl) == n) //检查被插入的通知链实例是否已经存在,避免重复插入
- return 0;
- 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;
- }
- /* linux-2.6.38.8/include/linux/rcupdate.h */
- #define rcu_assign_pointer(p, v) \
- __rcu_assign_pointer((p), (v), __rcu)
- #define __rcu_assign_pointer(p, v, space) \
- ({ \
- if (!__builtin_constant_p(v) || \
- ((v) != NULL)) \
- smp_wmb(); \
- (p) = (typeof(*v) __force space *)(v); \
- })
4、触发相应的处理函数 (即使用链表中的通知链实例)
- /* linux-2.6.38.8/include/linux/notifier.h */
- extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
- unsigned long val, void *v);
- extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh,
- unsigned long val, void *v, int nr_to_call, int *nr_calls);
- extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
- unsigned long val, void *v);
- extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh,
- unsigned long val, void *v, int nr_to_call, int *nr_calls);
- extern int raw_notifier_call_chain(struct raw_notifier_head *nh,
- unsigned long val, void *v);
- extern int __raw_notifier_call_chain(struct raw_notifier_head *nh,
- unsigned long val, void *v, int nr_to_call, int *nr_calls);
- extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
- unsigned long val, void *v);
- extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh,
- unsigned long val, void *v, int nr_to_call, int *nr_calls);
它们最后都会调用notifier_call_chain函数。
- /* linux-2.6.38.8/kernel/notifier.c */
- static int __kprobes 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) { //根据需要,会调用链表中nr_to_call个通知链实例的处理函数,优先级最高的将被最先调用
- next_nb = rcu_dereference_raw(nb->next);
- #ifdef CONFIG_DEBUG_NOTIFIERS
- if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
- WARN(1, "Invalid notifier called!");
- nb = next_nb;
- continue;
- }
- #endif
- ret = nb->notifier_call(nb, val, v); //调用通知链实例的处理函数
- if (nr_calls)
- (*nr_calls)++;
- if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
- break;
- nb = next_nb;
- nr_to_call--;
- }
- return ret;
- }