参考:内核通知链
注:我们对于一个比较好的结构,先学会用,然后尝试自己去实现即可。
相关类图:notifier.drawio
内核通知链的使用
内核通知链的作用
内核的通知链就是:“订阅者-发布者”模型
作用:就是注册一个通知链表(基于优先级排序),在某种情况发证的时候,遍历去通知下大家;
就像考试的时候,在好学生答完卷以后(发生了事件),然后将答案一个个往后传(通知),最后大家都完成了;
实现基于 事件和 优先级, 优先级越高越快(notifier_chain_register 链表添加时遍历优先级,priority 高的插在链表前边)
内核相关API介绍
根据上边的情况,其实也就分为三个接口:
申请加入被通知队列,申请退出被通知队列;触发一次通知
其实就是单向链表的:插入,删除,遍历;
首先,内核支持三种通知链:
atomic使用自旋锁,blocking使用rwsem(可阻塞的锁),RAW原始通知链用户自定义锁
头文件include/linux/notifier.h,
源文件kernel/notifier.c
#include <linux/notifier.h>
/*********************** 1. 定义通知链头部********************/
// 初始化通知链header
ATOMIC_NOTIFIER_HEAD(name) //定义并初始化一个名为name的原子通知链
BLOCKING_NOTIFIER_HEAD(name) //定义并初始化一个名为name的阻塞通知链
RAW_NOTIFIER_HEAD(name) //定义并初始化一个名为name的原始通知链
// 或者
static struct atomic_notifier_head dock_notifier_list;
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
static struct blocking_notifier_head dock_notifier_list;
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
static struct raw_notifier_head dock_notifier_list;
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
/*********************** 2.定义通知链毁掉函数********************/
nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
if (nb == NULL) {
pr_err("......\n");
return;
}
nb->notifier_call = xxx;
/*********************** 3. 使用通知链********************/
//原子通知链
int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *nb);
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *nb);
int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
//可阻塞通知链
int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *nb);
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,unsigned long val, void *v);
int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);
int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *nb);
int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *nb);
//原始通知链
int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *nb);
int raw_notifier_chain_unregister(struct raw_notifier_head *nh,struct notifier_block *nb);
int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);
// 如果在总线中,想通知到自己,就不继续通知后边的人了,怎么办?
自己在回调函数中返回:
return NOTIFY_STOP;
DEMO
内核通知链:中举了一个很典型的例子;
测试源码 已整理到gittee
典型案例
Linux总线框架就用了notifier机制 代码实现细节:
bus_register
// 每一个总线初始化一个notifier head,这里使用阻塞接口
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
dma_debug_add_bus
nb->notifier_call = dma_debug_device_change; // 总线回调函数
// 给总线的notifier head添加一个 通知需求
bus_register_notifier
blocking_notifier_chain_register(&bus->p->bus_notifier, nb);
内核通知链的实现细节
notifier_chain_register 是一个单项链表,然后
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v)
nh 是自己的通知链头
val 是 自定义的命令,被 回调函数解析;
v 是用户自定义参数
可参考 driver/base/core.c中device_add接口
在用户层实现一个内核通知链
TBD