引入在linux内核中,各个子系统之间有很强的相互关系,某些子系统可能对其他子系统产生的事件比较感兴趣 。因此内核引入了notifier机制,当然了notifier机制只能用在内核子系统之间,不能用在内核与应用层之间 。比如当系统suspend的时候,就会使用到notifier机制来通知系统的内核线程进行suspend 。
嵌入式进阶教程分门别类整理好了,看的时候十分方便,由于内容较多,这里就截取一部分图吧 。
文章插图
需要的朋友私信【内核】即可领取 。
内核学习地址:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂
内核实现的notifier机制代码位于kernel/kernel/notifier.c,同时此机制的代码量也不是很多只有600行左右 。
数据结构内核使用struct notifier_block结构代表一个notifier
typedef int (*notifier_fn_t)(struct notifier_block *nb,unsigned long action, void *data);struct notifier_block { notifier_fn_t notifier_call; struct notifier_block __rcu *next; int priority;};
notifier_call: 代表当事件发生之后调用的回调函数 。next: 用来链接同一个类型的notifier 。
priority: notifier chain的优先级 。对应的数字越大优先级越高,就优先执行 。
同时内核也提供了四种不同类型的notifier chain
- 原子通知链(Atomic notifier chains)
struct atomic_notifier_head { spinlock_t lock; struct notifier_block __rcu *head;};
可以看到原子notifier chain只是对notifier_block的一个封装 。同时atomic notifier chain的回调函数需要运行在中断上下文/原子上下文中,而且不能睡眠 。很明显因为atomic_notifer_head其中的spin_lock的特点就是不能睡眠 。
- 可阻塞通知链(Blocking notifier chains)
struct blocking_notifier_head { struct rw_semaphore rwsem; struct notifier_block __rcu *head;};
blocking_notifier_head其中包含了读写信号量成员rwsem,而信号量的特定就是运行在进程上下文,而且还可以睡眠 。同理Blocking notifier chains的回调函数特征一样 。- 原始通知链(Raw notifier chains)
struct raw_notifier_head { struct notifier_block __rcu *head;};
raw_notifier_head的特点是对回调函数,register, unregister都没有任何限制,所有的保护机制都需要调用者维护 。- SRCU通知链(SRCU notifier chains)
struct srcu_notifier_head { struct mutex mutex; struct srcu_struct srcu; struct notifier_block __rcu *head;};
SRCU通知链是block notifier chain的一种变体,采用SRCU(Sleepable Read-Copy Update)代替rw-semphore来保护chainsnotifier chain初始化内核提供了一套宏用来初始化各个类型的通知链
#define ATOMIC_INIT_NOTIFIER_HEAD(name) do {spin_lock_init(&(name)->lock);(name)->head = NULL;} while (0)#define BLOCKING_INIT_NOTIFIER_HEAD(name) do {init_rwsem(&(name)->rwsem);(name)->head = NULL;} while (0)#define RAW_INIT_NOTIFIER_HEAD(name) do {(name)->head = NULL;} while (0)
以上是动态初始化各个类型的通知链,当然了有动态初始化,也就有静态初始化#define ATOMIC_NOTIFIER_INIT(name) {.lock = __SPIN_LOCK_UNLOCKED(name.lock),.head = NULL }#define BLOCKING_NOTIFIER_INIT(name) {.rwsem = __RWSEM_INITIALIZER((name).rwsem),.head = NULL }#define RAW_NOTIFIER_INIT(name) {.head = NULL }/* srcu_notifier_heads cannot be initialized statically */#define ATOMIC_NOTIFIER_HEAD(name)struct atomic_notifier_head name =ATOMIC_NOTIFIER_INIT(name)#define BLOCKING_NOTIFIER_HEAD(name)struct blocking_notifier_head name =BLOCKING_NOTIFIER_INIT(name)#define RAW_NOTIFIER_HEAD(name)struct raw_notifier_head name =RAW_NOTIFIER_INIT(name)
通过注释可以知道SRCU通知链不能使用静态的方法,因此内核提供了一个动态的初始化函数,void srcu_init_notifier_head(struct srcu_notifier_head *nh){ mutex_init(&nh->mutex); if (init_srcu_struct(&nh->srcu) < 0)BUG(); nh->head = NULL;}
注册/注销通知链内核提供的最基本的注册通知链的函数/* * Notifier chain core routines.The exported routines below * are layered on top of these, with Appropriate locking added. */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;}
上述的操作就是通过判断priority的大小,然后将大的插入带链表头,小的插入在链表末尾 。推荐阅读
- MacBook|苹果最新自研芯片性能如何?一文了解M2版MacBook Air跑分详情
- 艺术品|和田玉最主要的分清点,搞懂什么是商品,什么是可观赏的艺术品
- 釉里红|一次搞懂12种陶瓷类型
- 拍照|2022年最大超级月亮今晚上演:手机如何拍月亮 一文看懂
- 一文打尽NMS技术
- 一文搞懂shell脚本
- 烫发|染发真的会致癌吗?染发和烫发哪个危害大?一文告诉你
- 一加|一加10T和一加10 Pro区别在哪?一文提前了解
- ARM|高通要用!一文了解ARMv9第二代Cortex核心:性能创新高
- 300块和3000块的眼镜,有啥区别?一文为你揭秘,涨知识了