由起
内核中许多自系统之间关系密切,因此在一个子系统发生或者检测到的事件信息很可能对其他子系统来说也很有价值。为了满足其他子系统对这些事件信息的需求(内核子系统之间),内核提供了notification chain机制。
notification chain的实现使用publish-subscribe模型,当事件发生时,检测或者产生时间的子系统作为主动一方通过通知函数来告知作为被动一方的订阅者(需要该信息的子系统)。subscriber提供callback函数给publisher调用。subscriber必须知道其他子系统提供了哪些事件通知支持,以选择可以订阅的事件通知。subscriber本身也可以具有信息发布的功能。当事件发生时,publisher通知subscriber,但是publisher对于谁subscribe事件通知和为何需要此事件信息,不关心。
实现
内核实现主要在~/include/linux/notifer.h和~/kernel/notifer.c中实现了该机制。
notification chain由单链表实现(没有使用内核通用的循环链表),每个链表项是notifier_block结构体。
struct notifier_block { int (*notifier_call)(struct notifier_block *, unsigned long, void *); struct notifier_block __rcu *next; int priority; }; <~/include/linux/notifer.h>
notifier_call,subscriber提供给publisher的回调函数。第一个参数为notifer_block本身,第二个参数表示时间发生的类型,因为一个chain可能支持多个事件,在该文件中设置了一些时间常量,第三个参数存放私有信息,与具体的事件相关。
priority:表示该回调函数的优先级,事件发生时,高优先级的回调函数先得到调用。具体作用在notifier_chain_register函数中可以得到体现。
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; } <~/kernel/notifer.c>
该函数的意义就十分明显了,根据block的优先级顺序,将其插入到指定的链中。
与之对应的,notifier_chain_unregister
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; }
当指定的时间发生时,调用相应的通知链,告知subscriber事件的发生。
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) { 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 //基本上所有的回调函数里面都会使用switch语句,通过判断不同的event值来执行不同的操作。 ret = nb->notifier_call(nb, val, v); if (nr_calls) (*nr_calls)++; ///回调函数可能中断对其他低优先级subscriber的通知 if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) break; nb = next_nb; nr_to_call--; } ///返回值为最后一个回调函数执行的返回值 return ret; }
多数子系统都定义了这些基本例程的封装函数,因此很少看到对这些函数的直接调用。