参考:内核通知链
注:我们对于一个比较好的结构,先学会用,然后尝试自己去实现即可。

相关类图:notifier.drawio

内核通知链的使用

内核通知链的作用

内核的通知链就是:“订阅者-发布者”模型

作用:就是注册一个通知链表(基于优先级排序),在某种情况发证的时候,遍历去通知下大家;

就像考试的时候,在好学生答完卷以后(发生了事件),然后将答案一个个往后传(通知),最后大家都完成了;

image.png

实现基于 事件和 优先级, 优先级越高越快(notifier_chain_register 链表添加时遍历优先级,priority 高的插在链表前边)

内核相关API介绍

根据上边的情况,其实也就分为三个接口:
申请加入被通知队列,申请退出被通知队列;触发一次通知
其实就是单向链表的:插入,删除,遍历;

首先,内核支持三种通知链:
atomic使用自旋锁,blocking使用rwsem(可阻塞的锁),RAW原始通知链用户自定义锁
头文件include/linux/notifier.h,
源文件kernel/notifier.c

  1. #include <linux/notifier.h>
  2. /*********************** 1. 定义通知链头部********************/
  3. // 初始化通知链header
  4. ATOMIC_NOTIFIER_HEAD(name) //定义并初始化一个名为name的原子通知链
  5. BLOCKING_NOTIFIER_HEAD(name) //定义并初始化一个名为name的阻塞通知链
  6. RAW_NOTIFIER_HEAD(name) //定义并初始化一个名为name的原始通知链
  7. // 或者
  8. static struct atomic_notifier_head dock_notifier_list;
  9. ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
  10. static struct blocking_notifier_head dock_notifier_list;
  11. ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
  12. static struct raw_notifier_head dock_notifier_list;
  13. ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
  14. /*********************** 2.定义通知链毁掉函数********************/
  15. nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
  16. if (nb == NULL) {
  17. pr_err("......\n");
  18. return;
  19. }
  20. nb->notifier_call = xxx;
  21. /*********************** 3. 使用通知链********************/
  22. //原子通知链
  23. int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *nb);
  24. int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *nb);
  25. int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
  26. //可阻塞通知链
  27. int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
  28. int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
  29. int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *nb);
  30. int blocking_notifier_call_chain(struct blocking_notifier_head *nh,unsigned long val, void *v);
  31. int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);
  32. int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *nb);
  33. int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *nb);
  34. //原始通知链
  35. int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *nb);
  36. int raw_notifier_chain_unregister(struct raw_notifier_head *nh,struct notifier_block *nb);
  37. int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);
  38. // 如果在总线中,想通知到自己,就不继续通知后边的人了,怎么办?
  39. 自己在回调函数中返回:
  40. return NOTIFY_STOP;

DEMO

内核通知链:中举了一个很典型的例子;
测试源码 已整理到gittee

典型案例

Linux总线框架就用了notifier机制 10.内核通知链 - 图2 代码实现细节:

  1. bus_register
  2. // 每一个总线初始化一个notifier head,这里使用阻塞接口
  3. BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
  4. dma_debug_add_bus
  5. nb->notifier_call = dma_debug_device_change; // 总线回调函数
  6. // 给总线的notifier head添加一个 通知需求
  7. bus_register_notifier
  8. blocking_notifier_chain_register(&bus->p->bus_notifier, nb);

内核通知链的实现细节

image.png
notifier_chain_register 是一个单项链表,然后

  1. int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
  2. unsigned long val, void *v)
  3. nh 是自己的通知链头
  4. val 自定义的命令,被 回调函数解析;
  5. v 是用户自定义参数
  6. 可参考 driver/base/core.cdevice_add接口

在用户层实现一个内核通知链

TBD