notifier chain — 内核通知链【转】
转自:http://blog.csdn.net/g_salamander/article/details/8081724
大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。通知链技术可以概括为:事件的接收者将事件发生时应该执行的操作通过函数指针方式保存在链表中,然后当事件发生时通知者依次执行链表中每一个元素的回调函数。
一、notifier chain 定义和接口
- struct notifier_block {
- int (*notifier_call)(struct notifier_block *, unsigned long, void *); // 回调函数接口
- struct notifier_block *next; // 指向下一个通知结构
- int priority; // 当前通知链的优先级
- };
可以看到通知链的基础数据结构比较简单,有回调函数接口、下一节点指针、优先级三个成员,其中回调函数的三个参数分别为:指向当前结构的指针、事件类型、参数;内核提供了4种常用的通知链,分别为:
1、Atomic notifier chains
原子通知链:通知链元素的回调函数(当事件发生时要执行的函数)只能在中断上下文中运行,不允许阻塞
- struct atomic_notifier_head {
- spinlock_t lock;
- struct notifier_block *head;
- };
由宏 ATOMIC_NOTIFIER_HEAD(name) 初始化链表头,其注册、注销及通知接口分别为:
- int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n);
- int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n);
- int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);
2、Blocking notifier chains
可阻塞通知链:通知链元素的回调函数在进程上下文中运行,允许阻塞
- struct blocking_notifier_head {
- struct rw_semaphore rwsem;
- struct notifier_block *head;
- };
由宏 BLOCKING_NOTIFIER_HEAD(name) 初始化链表头,其注册、注销及通知接口分别为:
- int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
- int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *nb);
- int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);
3、Raw notifier chains
原始通知链:对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护
- struct raw_notifier_head {
- struct notifier_block *head;
- };
由宏 RAW_NOTIFIER_HEAD(name) 初始化链表头,其注册、注销及通知接口分别为:
- 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);
4、SRCU notifier chains
可阻塞通知链的一种变体
- struct srcu_notifier_head {
- struct mutex mutex;
- struct srcu_struct srcu;
- struct notifier_block *head;
- };
该链表头必须动态申请 srcu_init_notifier_head 和释放 srcu_cleanup_notifier_head,其注册、注销及通知接口分别为:
- int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *nb);
- int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *nb);
- int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);
5、notifier_call_chain
当有事件触发时,通知者调用 notifier_call_chain 函数通知事件的到达,这个函数会遍历nl指向的通知链中所有的元素,然后依次调用每一个的回调函数,完成通知动作。
- /**
- * notifier_call_chain - Informs the registered notifiers about an event.
- * @nl: Pointer to head of the blocking notifier chain
- * @val: Value passed unmodified to notifier function
- * @v: Pointer passed unmodified to notifier function
- * @nr_to_call: Number of notifier functions to be called. Don't care
- * value of this parameter is -1.
- * @nr_calls: Records the number of notifications sent. Don't care
- * value of this field is NULL.
- * @returns: notifier_call_chain returns the value returned by the
- * last notifier function called.
- */
- static int __kprobes notifier_call_chain(struct notifier_block **nl,
- unsigned long val, void *v,
- int nr_to_call, int *nr_calls)
- {
- int ret = NOTIFY_DONE;
- struct notifier_block *nb, *next_nb;
- nb = rcu_dereference(*nl);
- while (nb && nr_to_call) {
- next_nb = rcu_dereference(nb->next);
- #ifdef CONFIG_DEBUG_NOTIFIERS
- if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
- WARN(1, "Invalid notifier called!");
- nb = next_nb;
- continue;
- }
- #endif
- ret = nb->notifier_call(nb, val, v);
- if (nr_calls)
- (*nr_calls)++;
- if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
- break;
- nb = next_nb;
- nr_to_call--;
- }
- return ret;
- }
参数nl是通知链的头部,val表示事件类型,v用来指向通知链上的函数执行时需要用到的参数,一般不同的通知链,参数类型也不一样,例如当通知一个网卡被注册时,v就指向net_device结构,nr_to_call表示准备最多通知几个,-1表示整条链都通知,nr_calls非空的话,返回通知了多少个。每个被执行的 notifier_block 回调函数的返回值可能取值为以下几个:
NOTIFY_DONE:表示对相关的事件类型不关心
NOTIFY_OK:顺利执行
NOTIFY_BAD:执行有错
NOTIFY_STOP:停止执行后面的回调函数
NOTIFY_STOP_MASK:停止执行的掩码
notifier_call_chain 把最后一个被调用的回调函数的返回值作为它的返回值。
二、notifier chain 使用方法
在常见的环境中,我们通常会对 notifier chain 做一定的封装再使用。比如在电源管理子系统中,做了如下的定义和封装:
- static BLOCKING_NOTIFIER_HEAD(pm_chain_head); // 初始化链表头部
- int register_pm_notifier(struct notifier_block *nb) // 注册函数
- {
- return blocking_notifier_chain_register(&pm_chain_head, nb);
- }
- EXPORT_SYMBOL_GPL(register_pm_notifier);
- int unregister_pm_notifier(struct notifier_block *nb) // 注销函数
- {
- return blocking_notifier_chain_unregister(&pm_chain_head, nb);
- }
- EXPORT_SYMBOL_GPL(unregister_pm_notifier);
- int pm_notifier_call_chain(unsigned long val) // 通知函数
- {
- return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
- == NOTIFY_BAD) ? -EINVAL : 0;
- }
在使用的时候直接调用这几个接口则更为方便:
- static struct notifier_block ledtrig_sleep_pm_notifier = {
- .notifier_call = ledtrig_sleep_pm_callback, // 回调函数
- .priority = 0, // 优先级
- };
- register_pm_notifier(&ledtrig_sleep_pm_notifier); // 注册到通知链
- unregister_pm_notifier(&ledtrig_sleep_pm_notifier); // 从通知链注销
- pm_notifier_call_chain(PM_POST_SUSPEND); // 调用通知链
notifier chain — 内核通知链【转】的更多相关文章
- linux kernel notifier chain(事件通知链)
Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制(notif ...
- [Linux] 内核通知链 notifier
Linux 内核中每个模块之间都是独立的,如果模块需要感知其他模块的事件,就需要用到内核通知链. 最典型的通知链应用就是 LCD 和 TP 之间,TP 需要根据 LCD 的亮灭来控制是否打开关闭触摸功 ...
- Linux内核调试方法总结之内核通知链
Linux内核通知链notifier 1.内核通知链表简介(引用网络资料) 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在 ...
- Linux 内核通知链随笔【中】
关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不同子系统之间的通信,通知链只能用于内核不同子系统之间的通信.那么内核通知链到底是怎么工作的?我们如何才能用好通知链? ...
- Linux 内核通知链随笔【中】【转】
转自:http://blog.chinaunix.net/uid-23069658-id-4364171.html 关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不 ...
- Linux内核通知链模块
通知链描写叙述 大多数内核子系统都是相互独立的,因此某个子系统可能对其他子系统产生的事件感兴趣. 为了满足这个需求,也即是让某个子系统在发生某个事件时通知其他的子系统.Linux内核提供了通知链的机制 ...
- Linux 内核通知链机制的原理及实现
一.概念: 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子 系统,Linux内核提供了通知链的机制.通 ...
- Linux内核通知链机制的原理及实现【转】
转自:http://www.cnblogs.com/armlinux/archive/2011/11/11/2396781.html 一.概念: 大多数内核子系统都是相互独立的,因此某个子系统可能对其 ...
- Linux内核基础--事件通知链(notifier chain)
转载: http://blog.csdn.net/wuhzossibility/article/details/8079025 http://blog.chinaunix.net/uid-277176 ...
随机推荐
- .Net MVC 实现长轮询
什么是长轮询? 长轮询是“服务器推”技术实现方式的一种,可以将服务端发生的变化实时传送到客户端而无须客户端频繁的地刷新.发送请求. 长轮询原理? 客户端向服务器发送Ajax请求,服务器接收到请求后,保 ...
- [洛谷P4070][SDOI2016]生成魔咒
题目大意:有一个字符串,每次在末尾加入一个字符,问当前共有多少个本质不同的字串 题解:$SAM$,就是问插入这个字符后,多了多少个字串,就是当前这个点的$Right$数组大小. 卡点:无 C++ Co ...
- selenium - 获取断言信息
断言:通过脚本提取相应元素的数值,将实际结果与预期结果进行比较.通常获取title,URL,text等信息进行断言. from selenium import webdriver from time ...
- hzwer分块九题(暂时持续更新)
hzwer分块9题 分块1:区间加法,单点查询 Code #include<bits/stdc++.h> #define in(i) (i=read()) using namespace ...
- Codeforces Round #547 (Div. 3) 题解
Codeforces Round #547 (Div. 3) 题目链接:https://codeforces.com/contest/1141 A,B咕咕了... C. Polycarp Restor ...
- redis缓存在项目中的使用
关于redis为什么能作为缓存这个问题我们就不说了,直接来说一下redis缓存到底如何在项目中使用吧: 1.redis缓存如何在项目中配置? 1.1redis缓存单机版和集群版配置?(redis的客户 ...
- zabbix监控ipmi
@1.假设idrac已经启用,并且使用ipmitool能正确读取数据(本文后面详细描述如何配置) @2.第一节只涉及web配置 1.创建主机 configuration-->hosts--> ...
- 手脱ASProtect v1.2(无Stolen Code)
1.载入PEID ASProtect v1.2 2.载入OD > 01C04200 push 跑跑赛道.0042C001 ; //入口处 C3 retn AA stos byte ptr es: ...
- 解决iText2.0.8+freemark导出pdf不支持base64的解决办法
转换工具类 public class B64ImgReplacedElementFactory implements ReplacedElementFactory { /** * 实现createRe ...
- vs报错“以下文件中的行尾不一致,是否将行尾标准化”
vs报错"以下文件中的行尾不一致,是否将行尾标准化" 分析: 通过读取源文件,发现换行都使用的是"\n" Windows和Unix不同的标准引起的...即& ...