Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notificationchain)。
通知链只能用在各个子系统之间,而不能在内核和用户空间进行事件的通知。
组成内核的核心系统代码均位于kernel目录下,通知链表位于kernel/notifier.c中,对应的头文件为include/linux/notifier.h。
notifier_block是通知链中的主要结构:
struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
struct notifier_block *next;
int priority;
};
其中,
a. notifier_call:当相应事件发生时应该调用的函数,由被通知方提供;
b. notifier_block *next:用于链接成链表的指针;
c. priority:回调函数的优先级,一般默认为0;
围绕核心数据结构notifier_block,内核定义了四种通知链类型:
a. 原子通知链( Atomic notifier chains ):通知链元素的回调函数(当事件发生时要执行的函数)在中断或原子操作上下文中运行,不允许阻塞。对应的链表头结构:
struct atomic_notifier_head {
spinlock_t lock;
struct notifier_block *head;
};
b. 可阻塞通知链( Blocking notifier chains ):通知链元素的回调函数在进程上下文中运行,允许阻塞。对应的链表头:
struct blocking_notifier_head {
struct rw_semaphore rwsem;
struct notifier_block *head;
};
c. 原始通知链( Raw notifierchains ):对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护。对应的链表头:
struct raw_notifier_head {
struct notifier_block *head;
};
d. SRCU 通知链( SRCU notifier chains ):可阻塞通知链的一种变体。对应的链表头:
struct srcu_notifier_head {
struct mutex mutex;
struct srcu_struct srcu;
struct notifier_block *head;
};
a. 通知方先初始化一个通知链;
b. 被通知方申明一个notifier_block;
c. 被通知方实现notifier_call函数;
d. 被通知方调用特定的事件通知链的注册函数,将notifier_block注册到通知方的通知链中。特定的注册函数包括:
int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n)
int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n)
int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n)
int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *n)
e. 当通知方状态变化时,调用对应的notifier_call_chain函数通知其他子系统,对应的notifier_call_chain包括:
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)
f. 被通知方执行notifier_block函数;
notifier_block的返回值是NOTIFY_XXX的形式,在include/linux/notifier.h中:
#define NOTIFY_DONE 0x0000 /* 对事件视而不见 */
#define NOTIFY_OK 0x0001 /* 事件正确处理 */
#define NOTIFY_STOP_MASK 0x8000 /*由notifier_call_chain检查,看继续调用回调函数,还是停止,_BAD和_STOP中包含该标志 */
#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) /*事件处理出错,不再继续调用回调函数 */
/*
*Clean way to return from the notifier and stop further calls.
*/
#define NOTIFY_STOP (NOTIFY_OK|NOTIFY_STOP_MASK) /* 回调出错,不再继续调用该事件回调函数 */