开发者社区> sky-heaven> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

notifier chain — 内核通知链【转】

简介: 转自:http://blog.csdn.net/g_salamander/article/details/8081724 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。
+关注继续查看

转自: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.   
  20.     nb = rcu_dereference(*nl);  
  21.   
  22.     while (nb && nr_to_call) {  
  23.         next_nb = rcu_dereference(nb->next);  
  24.   
  25. #ifdef CONFIG_DEBUG_NOTIFIERS  
  26.         if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {  
  27.             WARN(1, "Invalid notifier called!");  
  28.             nb = next_nb;  
  29.             continue;  
  30.         }  
  31. #endif  
  32.         ret = nb->notifier_call(nb, val, v);  
  33.   
  34.         if (nr_calls)  
  35.             (*nr_calls)++;  
  36.   
  37.         if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)  
  38.             break;  
  39.         nb = next_nb;  
  40.         nr_to_call--;  
  41.     }  
  42.     return ret;  
  43. }  

参数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.   
  3. int register_pm_notifier(struct notifier_block *nb)   // 注册函数  
  4. {  
  5.     return blocking_notifier_chain_register(&pm_chain_head, nb);  
  6. }  
  7. EXPORT_SYMBOL_GPL(register_pm_notifier);  
  8.   
  9. int unregister_pm_notifier(struct notifier_block *nb)  // 注销函数  
  10. {  
  11.     return blocking_notifier_chain_unregister(&pm_chain_head, nb);  
  12. }  
  13. EXPORT_SYMBOL_GPL(unregister_pm_notifier);  
  14.   
  15. int pm_notifier_call_chain(unsigned long val)          // 通知函数  
  16. {  
  17.     return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)  
  18.             == NOTIFY_BAD) ? -EINVAL : 0;  
  19. }  

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

[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.   
    6. register_pm_notifier(&ledtrig_sleep_pm_notifier);    // 注册到通知链  
    7.   
    8. unregister_pm_notifier(&ledtrig_sleep_pm_notifier);  // 从通知链注销  
    9.   
    10. pm_notifier_call_chain(PM_POST_SUSPEND);             // 调用通知链  
【作者】张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
ctfshow-萌新-web5( 利用位运算符控制SQL获取网站敏感信息)
ctf.show 萌新模块 we5关,这一关考察的是intval()函数转换字符串的特性和SQL注入的绕过思路,这一关过滤了单双引号,or,斜杠,加减乘除号,叹号,括号,select等关键字,推荐使用取反运算符(~)来控制SQL语句,获取flag
117 0
SAP Analytics Cloud关于Smart Predict功能的说明
SAP Analytics Cloud关于Smart Predict功能的说明
52 0
浏览器开启桌面通知Web Notification
本文主要描述如何开启各个浏览器的桌面通知功能 一、谷歌浏览器(chrome) 点击地址栏前面的图标 或者 ⓘ,修改通知即可 二、360浏览器 在地址栏输入 se://settings/content,找到通知进行设置 三、火狐浏览器(Mozilla Firefox) 您可以通过下列步骤设置站点的权限: 点击图标 打开 控制中心。
1487 0
Apple Notification Center Service--ANCS【转】
Apple Notification Center Service 转自:http://studentdeng.github.io/blog/2014/03/22/ancs/ MAR 22ND, 2014 | COMMENTS 名词解释与约定 名词解释 Apple Notification Center Service 简称 ANCS。
1011 0
Web APi之认证(Authentication)两种实现方式【二】(十三)
前言 上一节我们详细讲解了认证及其基本信息,这一节我们通过两种不同方式来实现认证,并且分析如何合理的利用这两种方式,文中涉及到的基础知识,请参看上一篇文中,就不再叙述废话。 序言 对于所谓的认证说到底就是安全问题,在Web API中有多种方式来实现安全,【accepted】方式来处理基于IIS...
1001 0
linux下磁盘进行分区、文件系统创建、挂载和卸载(转)
任务的原因:由于,刚购买来的服务器需要将磁盘挂载到操作系统上,为了挂载磁盘首先要对磁盘进行分区,然后进行文件系统的创建,最后将磁盘挂载到操作系统上的某个目录。 MBR(Master Boot Record)是传统的分区机制,应用于绝大多数使用BIOS的PC设备。
1008 0
+关注
sky-heaven
我是一个技术爱好者,喜欢分享交流技术心得
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载