转自:http://blog.csdn.net/g_salamander/article/details/8081724

大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。通知链技术可以概括为:事件的接收者将事件发生时应该执行的操作通过函数指针方式保存在链表中,然后当事件发生时通知者依次执行链表中每一个元素的回调函数。

一、notifier chain 定义和接口

[cpp] view plain copy
  1. struct notifier_block {
  2. int (*notifier_call)(struct notifier_block *, unsigned long, void *);  // 回调函数接口
  3. struct notifier_block *next;  // 指向下一个通知结构
  4. int priority;                 // 当前通知链的优先级
  5. };

可以看到通知链的基础数据结构比较简单,有回调函数接口、下一节点指针、优先级三个成员,其中回调函数的三个参数分别为:指向当前结构的指针、事件类型、参数;内核提供了4种常用的通知链,分别为:

1、Atomic notifier chains

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

[cpp] view plain copy
  1. struct atomic_notifier_head {
  2. spinlock_t lock;
  3. struct notifier_block *head;
  4. };

由宏 ATOMIC_NOTIFIER_HEAD(name) 初始化链表头,其注册、注销及通知接口分别为:

[cpp] view plain copy
  1. int atomic_notifier_chain_register(struct atomic_notifier_head *nh, struct notifier_block *n);
  2. int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *n);
  3. int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);

2、Blocking notifier chains

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

[cpp] view plain copy
  1. struct blocking_notifier_head {
  2. struct rw_semaphore rwsem;
  3. struct notifier_block *head;
  4. };

由宏 BLOCKING_NOTIFIER_HEAD(name) 初始化链表头,其注册、注销及通知接口分别为:

[cpp] view plain copy
  1. int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
  2. int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *nb);
  3. int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);

3、Raw notifier chains

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

[cpp] view plain copy
  1. struct raw_notifier_head {
  2. struct notifier_block *head;
  3. };

由宏 RAW_NOTIFIER_HEAD(name) 初始化链表头,其注册、注销及通知接口分别为:

[cpp] view plain copy
  1. int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *nb);
  2. int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *nb);
  3. int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);

4、SRCU notifier chains

可阻塞通知链的一种变体

[cpp] view plain copy
  1. struct srcu_notifier_head {
  2. struct mutex mutex;
  3. struct srcu_struct srcu;
  4. struct notifier_block *head;
  5. };

该链表头必须动态申请 srcu_init_notifier_head 和释放 srcu_cleanup_notifier_head,其注册、注销及通知接口分别为:

[cpp] view plain copy
  1. int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *nb);
  2. int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *nb);
  3. int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);

5、notifier_call_chain
当有事件触发时,通知者调用 notifier_call_chain 函数通知事件的到达,这个函数会遍历nl指向的通知链中所有的元素,然后依次调用每一个的回调函数,完成通知动作。

[cpp] view plain copy
  1. /**
  2. * notifier_call_chain - Informs the registered notifiers about an event.
  3. *  @nl:        Pointer to head of the blocking notifier chain
  4. *  @val:       Value passed unmodified to notifier function
  5. *  @v:     Pointer passed unmodified to notifier function
  6. *  @nr_to_call:    Number of notifier functions to be called. Don't care
  7. *          value of this parameter is -1.
  8. *  @nr_calls:  Records the number of notifications sent. Don't care
  9. *          value of this field is NULL.
  10. *  @returns:   notifier_call_chain returns the value returned by the
  11. *          last notifier function called.
  12. */
  13. static int __kprobes notifier_call_chain(struct notifier_block **nl,
  14. unsigned long val, void *v,
  15. int nr_to_call, int *nr_calls)
  16. {
  17. int ret = NOTIFY_DONE;
  18. struct notifier_block *nb, *next_nb;
  19. nb = rcu_dereference(*nl);
  20. while (nb && nr_to_call) {
  21. next_nb = rcu_dereference(nb->next);
  22. #ifdef CONFIG_DEBUG_NOTIFIERS
  23. if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
  24. WARN(1, "Invalid notifier called!");
  25. nb = next_nb;
  26. continue;
  27. }
  28. #endif
  29. ret = nb->notifier_call(nb, val, v);
  30. if (nr_calls)
  31. (*nr_calls)++;
  32. if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
  33. break;
  34. nb = next_nb;
  35. nr_to_call--;
  36. }
  37. return ret;
  38. }

参数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 做一定的封装再使用。比如在电源管理子系统中,做了如下的定义和封装:

[cpp] view plain copy
  1. static BLOCKING_NOTIFIER_HEAD(pm_chain_head);         // 初始化链表头部
  2. int register_pm_notifier(struct notifier_block *nb)   // 注册函数
  3. {
  4. return blocking_notifier_chain_register(&pm_chain_head, nb);
  5. }
  6. EXPORT_SYMBOL_GPL(register_pm_notifier);
  7. int unregister_pm_notifier(struct notifier_block *nb)  // 注销函数
  8. {
  9. return blocking_notifier_chain_unregister(&pm_chain_head, nb);
  10. }
  11. EXPORT_SYMBOL_GPL(unregister_pm_notifier);
  12. int pm_notifier_call_chain(unsigned long val)          // 通知函数
  13. {
  14. return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)
  15. == NOTIFY_BAD) ? -EINVAL : 0;
  16. }

在使用的时候直接调用这几个接口则更为方便:

[cpp] view plain copy
  1. static struct notifier_block ledtrig_sleep_pm_notifier = {
  2. .notifier_call = ledtrig_sleep_pm_callback,      // 回调函数
  3. .priority = 0,                                   // 优先级
  4. };
  5. register_pm_notifier(&ledtrig_sleep_pm_notifier);    // 注册到通知链
  6. unregister_pm_notifier(&ledtrig_sleep_pm_notifier);  // 从通知链注销
  7. pm_notifier_call_chain(PM_POST_SUSPEND);             // 调用通知链
【作者】张昺华
【出处】http://www.cnblogs.com/sky-heaven/
【博客园】 http://www.cnblogs.com/sky-heaven/
【新浪博客】 http://blog.sina.com.cn/u/2049150530
【知乎】 http://www.zhihu.com/people/zhang-bing-hua
【我的作品---旋转倒立摆】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【我的作品---自平衡自动循迹车】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

notifier chain — 内核通知链【转】相关推荐

  1. 深入理解Linux内核通知链(Notifier)

    数据结构 内核使用struct notifier_block结构代表一个notifier typedef int (*notifier_fn_t)(struct notifier_block *nb, ...

  2. linux 内核通知,[Linux] 内核通知链 notifier

    Linux 内核中每个模块之间都是独立的,如果模块需要感知其他模块的事件,就需要用到内核通知链. 最典型的通知链应用就是 LCD 和 TP 之间,TP 需要根据 LCD 的亮灭来控制是否打开关闭触摸功 ...

  3. notifier_chain 内核通知链的学习与使用

    linux 内核通知链 通知链用于将状态改变信息发送给请求这些改变的代码段,通知链可以用于内核将特定的事件传递给感兴趣的内核组件中. 内核定义了主要的事件类型: 死亡通知 网络设备通知 cpu 频率通 ...

  4. 内核通知链(网络子系统为例)

    概念 1.Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制( ...

  5. Linux 内核通知链和例程代码

    概念 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制.通知链表只 ...

  6. Linux 内核通知链随笔【中】

        关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不同子系统之间的通信,通知链只能用于内核不同子系统之间的通信.那么内核通知链到底是怎么工作的?我们如何才能用好 ...

  7. Linux内核通知链(notifier chain)

    1.概述 Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制( ...

  8. linux 通知链,Linux内核通知链notifier

    当有事件发生时,通知者调用 notifier_call_chain 函数通知事件的到达,这个函数会遍历n1指向的通知链中所有的元素,然后依次调用每一个的回调函数,完成通知动作. static int ...

  9. Linux内核通知链机制的原理及实现【转】

    转自:http://www.cnblogs.com/armlinux/archive/2011/11/11/2396781.html 一.概念: 大多数内核子系统都是相互独立的,因此某个子系统可能对其 ...

最新文章

  1. 如何创建高质量的TypeScript声明文件(六) - 示例
  2. 轻量级嵌入式数据库H2的愉快玩耍之旅
  3. vue切换路由时动画
  4. window清理垃圾
  5. centos7;windows下安装和使用spice
  6. 蜗杆参数法设计_技术贴 | 减速器结构及设计的注意事项
  7. 动态规划在求解背包问题中的应用(JAVA)--回溯法、记忆化法
  8. linux修改最大进程,linux 进程数最大值修改方法
  9. 前端html小技巧(css篇)—表单美化详解
  10. 2019年5款你必须知道的顶级ASO优化工具
  11. 遇到服务器网络偶尔断线如何检查
  12. linux 指令熟悉
  13. SOFAServerless 体系助力业务极速研发
  14. HZ 和 usleep最小睡眠时间(低精度定时器)
  15. 电脑双屏显示变单屏后部分程序无法在当前屏幕显示的问题
  16. pythonocc_如何在pythonOCC中使用样条曲线?
  17. 基于LSTM的股票价格预测
  18. Web 3.0 中常见的网络钓鱼攻击
  19. Photoshop CS6基本知识1
  20. 同时导出多个excel,并且一个excel中包含多个sheet

热门文章

  1. English Through Movie
  2. 将文件放到Android模拟器的SD卡
  3. 计算机组成原理---之原码,补码,反码
  4. caffe学习日记--Lesson2:再看caffe的安装和使用、学习过程
  5. (六)基于霍夫变换的直线和圆检测
  6. 5.0 java集合框架中的接口collection属于_JAVA集合框架 - osc_cyo2dovg的个人空间 - OSCHINA - 中文开源技术交流社区...
  7. pycharm conda 环境 切换 linux_windows配置wsl2环境+pycharm指路
  8. 进入已经打开的pyrebox_PyREBox-可用Python编写脚本的逆向工程沙盒
  9. java 全选 反选取值_全选反选以及获取选中的数据
  10. 一行一个链接代码_小白写代码讨女朋友欢心,包教包会