大家好,我是王鸽,今天这篇文章主要是讲内核通知链函数的解读。其实阅读内核代码的时候,发现Linux内核有很多通用的函数,比如屏这块的控制。还有之前的insmod加载方式,kernel\module.c
从以上例子知道,核心函数是
staticint __kprobes notifier_call_chain(struct notifier_block **nl, unsignedlong val, void *v, int nr_to_call,int *nr_calls)
参数说明
参数 | 类型 | 说明 |
|---|
nl | struct notifier_block ** | 指向通知链头部的指针(链表头) |
val | unsigned long | 事件类型标识符(如 NETDEV_UP、CPU_DOWN_PREPARE 等) |
v | void * | 传递给回调函数的上下文数据(通常为事件相关的数据结构指针) |
返回值
返回值 | 说明 |
|---|
NOTIFY_DONE | 所有回调处理完毕,未中断链式调用 |
NOTIFY_STOP | 某个回调要求停止链式调用(例如返回 NOTIFY_STOP_MASK 掩码后的值) |
NOTIFY_BAD | 某个回调返回错误,链式调用被终止 |
在 Linux 内核中,notifier_call_chain()是一个用于触发通知链(Notifier Chain)的核心函数。它属于内核的事件通知机制,允许不同子系统注册回调函数,在特定事件(如系统关机、CPU 热插拔、内存压力等)发生时被调用。一.核心机制
1. 通知链(Notifier Chain)
通知链是 Linux 内核中一种订阅-发布模式的实现:发布者(Notifier Caller):通过notifier_call_chain()触发事件。
订阅者(Notifier Block):通过notifier_chain_register()注册回调函数到链中。
2. 数据结构
struct notifier_block { int (*notifier_call)(struct notifier_block *, unsigned long, void *); struct notifier_block *next; int priority; // 回调优先级(数值越小优先级越高) };
notifier_call:指向回调函数的指针。
next:链表指针,指向下一个节点。
priority:优先级,值越小优先级越高(或越大,取决于实现)。
二. 通知链类型
原子通知链(Atomic Notifier Chains):
使用自旋锁保护链表,回调函数在原子上下文(如中断)中执行,不可休眠。定义:struct atomic_notifier_head。可阻塞通知链(Blocking Notifier Chains):
使用互斥锁,回调函数在进程上下文中执行,允许休眠。定义:struct blocking_notifier_head。原始通知链(Raw Notifier Chains):
定义:struct raw_notifier_head。SRCU 通知链(SRCU Notifier Chains):
使用 Sleepable RCU 机制,适用于回调函数可能长时间阻塞的场景。定义:struct srcu_notifier_head。三. 关键操作
注册与注销
注册:将 notifier_block 插入链表中,按优先级排序。
intnotifier_chain_register(struct notifier_block **nl, struct notifier_block *n);
注销:从链表中移除指定notifier_block。intnotifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n);
事件通知
触发通知:遍历链表,依次调用回调函数。
intnotifier_call_chain(struct notifier_block **nl, unsignedlong val, void *v);
val:事件类型(如NETDEV_REGISTER)。
v:事件相关数据(如网络设备指针)。
回调函数返回值
NOTIFY_DONE:事件未处理,继续传递。
NOTIFY_OK:事件已处理,继续传递。
NOTIFY_STOP:事件处理完成,停止传递。
NOTIFY_BAD:事件错误,停止传递。
同步机制
原子通知链:通过自旋锁(spinlock_t)保护链表操作,确保原子性。
可阻塞通知链:使用互斥锁(struct mutex),允许休眠。
SRCU 通知链:利用 SRCU 的读-复制-更新机制,延迟释放资源以避免竞争。
典型应用场景
网络设备事件:如网卡注册(NETDEV_REGISTER)、状态变更(NETDEV_UP)。
// 注册网络设备通知链 static struct notifier_block netdev_notifier = { .notifier_call = netdev_event, .priority = 0, }; register_netdevice_notifier(&netdev_notifier);
典型应用例子
设备热插拔:通知驱动某个设备被插入或拔出。
网络状态变化:如网卡注册 (NETDEV_REGISTER)、IP地址变更。
电源管理:系统休眠/唤醒事件通知。
文件系统事件:如挂载/卸载文件系统。
内核中的具体实现
网络子系统:netdevice_notifier_chain 用于网络设备事件。
CPU热插拔:cpu_chain 通知 CPU 状态变化。
时钟事件:tick_notifier_list 处理时钟中断事件。
实现示例
性能与注意事项
回调函数应短小:避免在原子上下文中长时间运行。
避免嵌套锁:防止死锁,尤其在可阻塞链中。
优先级(Priority)
通过priority字段控制回调的执行顺序。高优先级(数值小)的回调先执行。
返回值处理
返回NOTIFY_STOP会终止链式调用(例如内核的die_chain在遇到panic()时终止)。返回NOTIFY_BAD表示严重错误(如内存分配失败)。
并发与锁
注册/注销操作需在安全上下文中进行(如模块初始化/退出时)。回调函数需考虑重入性和锁竞争(如避免阻塞操作)。
内核通知链类型
原子通知链(Atomic Notifier Chain):回调在原子上下文执行(不可睡眠)。
阻塞通知链(Blocking Notifier Chain):回调在进程上下文执行(可睡眠)。
原始通知链(Raw Notifier Chain):无锁保护,需自行处理并发。
通过上述机制,Linux内核实现了高效、灵活的事件通知系统,确保各模块在松散耦合的前提下协同工作。