欢迎光临散文网 会员登陆 & 注册

Linux内核基础 | 通知链机制

2022-08-01 15:42 作者:补给站Linux内核  | 我要投稿

一、通知链简介

举个形象的例子:将通知链比喻成”订阅者-发布者“,订阅者将感兴趣的公众号关注并设置提醒,发布者一旦发布某个文章,订阅者即可收到通知看到发布的内容。

在Linux内核中为了及时响应某些到来的事件,采取了通知链机制。该机制的两个角色的任务:

1、通知者定义通知链

2、被通知者向通知链中注册回调函数

3、当事件发生时,通知者发送通知 (执行通知链上每个调用块上的回调函数)所以通知链是一个单链表,单链表上的节点是调用块,每个调用块上有事件相关的回调函数和调用块的优先级。当事件触发时会按优先级顺序执行该链表上的回调函数。通知链只用于各个子系统之间,不能用于内核和用户空间进行事件的通知。

二、相关细节

1、通知链的类型

原子通知链( Atomic notifier chains ):

通知链元素的回调函数(当事件发生时要执行的函数)只能在中断上下文中运行,不允许阻塞。

可阻塞通知链( Blocking notifier chains ):

通知链元素的回调函数在进程上下文中运行,允许阻塞。

原始通知链( Raw notifier chains ):

对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护。

SRCU 通知链( SRCU notifier chains ):可阻塞通知链的一种变体

本文将以原子通知链进行分析


【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)      

2、原子通知链与通知块

初始化一个原子通知链使用以下宏定义

例如创建一个设备通知链队列头:

struct raw_notifier_head就相当于存放这条通知链单链表头,每一个通知链上的元素也就是通知块如下定义:

回调函数接口:

整个通知链的组织如下图所示:


3、向通知链中插入通知块

4、调用通知链

三、编写内核模块进行实验

1、案例1

编写内核模块作为被通知者,向内核netdev_chain通知链中插入自定义通知块(在通知块中自定义事件触发的回调函数),源码如下:

Makefile:

将模块插入内核后,将网卡关闭再重启一次,查看日志信息:

2、案例2

通过写两个内核模块,其中一个作为通知者一个作为被通知者

module_1.c:

  • 初始化一个通知链

  • 定义事件的回调函数并向通知链中插入三个通知块(与之前定义的回调函数相对应)

  • 测试通知链:循环遍历通知链的通知块,并同时调用对应的回调函数

module_2.c:模拟某事件发生,并调用通知链

(module_1与module_2的Makefile可参考上面的Demo1)

运行时先插入module_1再插入module_2结果如下,红框内是module_1中的测试输出日志,绿框内为世界调用通知链时的执行结果日志。


从上面可以看到通知链的执行顺序是按照优先级进行的,那么当调用通知链时是否每个通知块上的回调函数都会执行呢?

答案:不是,每个被执行的notifier_block回调函数的返回值可能取值以下几个:

  1. NOTIFY_DONE:表示对相关的事件类型不关心。

  2. NOTIFY_OK:顺利执行。

  3. NOTIFY_BAD:执行有错。

  4. NOTIFY_STOP:停止执行后面的回调函数。

  5. NOTIFY_STOP_MASK:停止执行的掩码

如当返回值NOTIFY_STOP_MASK会停止执行后面优先级低的调用块的函数。

例如把module_1中通知块的回调函数B_call的返回值修改为NOTIFY_STOP_MASK后,重新编译,运行结果如下,只执行了调用链中调用块2的回调函数。





Linux内核基础 | 通知链机制的评论 (共 条)

分享到微博请遵守国家法律