linux notifier

来源:互联网 发布:多处理器编程的艺术 编辑:程序博客网 时间:2024/05/01 01:41

Linux庞大系统中,各个模块是相对独立的,那么模块间通信该如何做呢?当然你也可以使用全局资源,如果这样的话系统缺少独立性,会带来稳定性问题的。如果你说,使用共享内存,进程通信等,那么你曲解我的意思了,因为你说的大多是user space的,而我说的是内核模块级别的。notifier_chain,对就是它,实质上这个机制就是一个回调函数链表的操作,回调函数的注册,注销,调用。源系统处(比如A子系统)进行定义初始化和回调函数调用,被通知处(比如B子系统)进行回调函数的注册和注销,那么当A系统发生某种事件是,就调用通知链中所有回调函数,B系统中注册的回调函数就会得到执行。一旦执行回调函数,它会从链表头依次执行每一个回调函数,那么依次执行是一次性全部执行完?执行过程的任意时间都可睡眠?这些需求也就产生了4种类型的notifier_chain。


结构体定义

[cpp] view plain copy
  1. struct notifier_block {     /* chain的基本单位 */  
  2.     int (*notifier_call)(struct notifier_block *, unsigned longvoid *);  
  3.     struct notifier_block __rcu *next;  
  4.     int priority;  
  5. };  
  6.   
  7. struct atomic_notifier_head {/* atmoic context; 执行(rcu_read_lock);回调函数执行不能阻塞;实时性高 */  
  8.     spinlock_t lock;  
  9.     struct notifier_block __rcu *head;  
  10. };  
  11.   
  12. struct blocking_notifier_head { /* process context;执行(rw_semaphore) ;回调函数执行可以阻塞;实时性相对低*/  
  13.     struct rw_semaphore rwsem;  
  14.     struct notifier_block __rcu *head;  
  15. };  
  16.   
  17. struct raw_notifier_head {  /* 原始链表操作,注册,执行过程无任何保护,完全有驱动人员控制 */  
  18.     struct notifier_block __rcu *head;  
  19. };  
  20.   
  21. struct srcu_notifier_head { /* process context;可阻塞通知链的变体(Sleepable Read-Copy-Update),回调函数执行可以阻塞 */  
  22.     struct mutex mutex;  
  23.     struct srcu_struct srcu;  
  24.     struct notifier_block __rcu *head;  
  25. };  
notifier_chain的API使用基本四大步骤:定义初始化、注册、调用和注销,而这些操作的基本单位是notifier_block,这个基本单位中包含了回调函数notifier_call,后继notifier_block指针,优先级priority(默认0,数字越大优先级越高,越靠近链表的头结点,越优先得到执行)。

初始化

#include <linux/notifier.h>

[cpp] view plain copy
  1. #define ATOMIC_NOTIFIER_HEAD(name)              \  
  2.     struct atomic_notifier_head name =          \  
  3.         ATOMIC_NOTIFIER_INIT(name)  
  4. #define BLOCKING_NOTIFIER_HEAD(name)                \  
  5.     struct blocking_notifier_head name =            \  
  6.         BLOCKING_NOTIFIER_INIT(name)  
  7. #define RAW_NOTIFIER_HEAD(name)                 \  
  8.     struct raw_notifier_head name =             \  
  9.         RAW_NOTIFIER_INIT(name)  
[cpp] view plain copy
  1. /* srcu_notifier_heads must be initialized and cleaned up dynamically */  
  2. extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);  
  3. #define srcu_cleanup_notifier_head(name)    \  
  4.         cleanup_srcu_struct(&(name)->srcu);  

经过定义及初始化后,一类的链表的头就形成了,注册就是增加节点,执行就是遍历节点,唯一要说明的就是,srcu链需要动态的定义,其他三中不需要。

注册和注销

[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)  
[cpp] view plain copy
  1. int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n);  
  2.     /* 注册的notifier_block不重复*/  
  3.     int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *n);  
  4.     int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *n);  
[cpp] view plain copy
  1. int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n);  
  2.     int raw_notifier_chain_unregister(struct raw_notifier_head *nh, struct notifier_block *n);  
[cpp] view plain copy
  1. int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *n);  
  2.     int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *n);  

其中注册和注销都调用了最基本的函数如下:

[cpp] view plain copy
  1. /* 
  2.  *  Notifier chain core routines.  The exported routines below 
  3.  *  are layered on top of these, with appropriate locking added. 
  4.  */  
  5.   
  6. static int notifier_chain_register(struct notifier_block **nl,  
  7.         struct notifier_block *n)  
  8. {  
  9.     while ((*nl) != NULL) {  
  10.         if (n->priority > (*nl)->priority)  
  11.             break;  
  12.         nl = &((*nl)->next);  
  13.     }  
  14.     n->next = *nl;  
  15.     rcu_assign_pointer(*nl, n);  
  16.     return 0;  
  17. }  
  18.   
  19. static int notifier_chain_cond_register(struct notifier_block **nl,  
  20.         struct notifier_block *n)  
  21. {  
  22.     while ((*nl) != NULL) {  
  23.         if ((*nl) == n)  
  24.             return 0;  
  25.         if (n->priority > (*nl)->priority)  
  26.             break;  
  27.         nl = &((*nl)->next);  
  28.     }  
  29.     n->next = *nl;  
  30.     rcu_assign_pointer(*nl, n);  
  31.     return 0;  
  32. }  
  33.   
  34. static int notifier_chain_unregister(struct notifier_block **nl,  
  35.         struct notifier_block *n)  
  36. {  
  37.     while ((*nl) != NULL) {  
  38.         if ((*nl) == n) {  
  39.             rcu_assign_pointer(*nl, n->next);  
  40.             return 0;  
  41.         }  
  42.         nl = &((*nl)->next);  
  43.     }  
  44.     return -ENOENT;  
  45. }  


调用

当A系统发生事件时,会调用下面函数之一的方法来,执行通知链中的所有回调函数,那么所有注册的地方都会得到执行,除前一个函数执行返回值含有NOTIFY_STOP_MASK标志。其中参数val是一个整形,用户自定义含义的传入值,参数v是一个void*类型,用户自定义任何类型含义,一般为平台结构体指针,使用的时候需要强制转换。

[cpp] view plain copy
  1. /*调用了__atomic_notifier_call_chain,且nr_to_call=-1表示回调函数调用个数不限,nr_calls=NULL表示不关心已执行的回调函数个数*/  
  2. extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);  
  3.     extern int __atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);  
  4.   
  5. extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v);  
  6. <span style="white-space:pre">    </span>extern int __blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);  
  7.   
  8. extern int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);  
  9.     extern int __raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);  
  10.   
  11. extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);  
  12.     extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v, int nr_to_call, int *nr_calls);  

回调函数返回值

[cpp] view plain copy
  1. #define NOTIFY_DONE     0x0000      /* Don't care回调函数不关心返回值*/  
  2. #define NOTIFY_OK       0x0001      /* Suits me 回调函数调用顺利完成*/  
  3. #define NOTIFY_STOP_MASK    0x8000      /* Don't call further 回调函数链禁止继续调用的掩码*/  
  4. #define NOTIFY_BAD      (NOTIFY_STOP_MASK|0x0002)  
  5.                         /* Bad/Veto action 回调函数执行有错*/  
  6. /* 
  7.  * Clean way to return from the notifier and stop further calls.当前顺利调用,禁止继续调用 
  8.  */  
  9. #define NOTIFY_STOP     (NOTIFY_OK|NOTIFY_STOP_MASK)  

在notifier_call_chain函数中去依次执行每一个注册的回调函数,并以前一个回调函数的返回值为判断依据,是否继续调用,最后一个执行函数的返回值作为notifier_call_chain的返回值。当前标准API中,只关注了NOTIFY_STOP_MASK,其他的在notifier_call_chain中未做处理。


典型用例

在linux中,液晶显示器会提供一个fb_notify,当显示器发生某种事件时,会调用notifier_call_chain,这样注册的回调函数得到执行,回调函数根据显示的framebuffer状态执行你预想的动作。
它选用通知链类型的是blocking_notifier_chain。
[cpp] view plain copy
  1. /* 
  2.  *  linux/drivers/video/fb_notify.c 
  3.  * 
  4.  *  Copyright (C) 2006 Antonino Daplas <adaplas@pol.net> 
  5.  * 
  6.  *  2001 - Documented with DocBook 
  7.  *  - Brad Douglas <brad@neruo.com> 
  8.  * 
  9.  * This file is subject to the terms and conditions of the GNU General Public 
  10.  * License.  See the file COPYING in the main directory of this archive 
  11.  * for more details. 
  12.  */  
  13. #include <linux/fb.h>  
  14. #include <linux/notifier.h>  
  15. #include <linux/export.h>  
  16.   
  17. /*静态定义并初始化通知链头*/  
  18. static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);  
  19.   
  20. /** 注册回调函数,加入一个回调函数节点 
  21.  *  fb_register_client - register a client notifier 
  22.  *  @nb: notifier block to callback on events 
  23.  */  
  24. int fb_register_client(struct notifier_block *nb)  
  25. {  
  26.     return blocking_notifier_chain_register(&fb_notifier_list, nb);  
  27. }  
  28. EXPORT_SYMBOL(fb_register_client);  
  29.   
  30. /** 注销回调函数,删除一个回调函数节点 
  31.  *  fb_unregister_client - unregister a client notifier 
  32.  *  @nb: notifier block to callback on events 
  33.  */  
  34. int fb_unregister_client(struct notifier_block *nb)  
  35. {  
  36.     return blocking_notifier_chain_unregister(&fb_notifier_list, nb);  
  37. }  
  38. EXPORT_SYMBOL(fb_unregister_client);  
  39.   
  40. /** 当源即framebuffer发生某种事件,调用该函数执行所有回调函数,通知其他子系统 
  41.  * fb_notifier_call_chain - notify clients of fb_events 
  42.  * 
  43.  */  
  44. int fb_notifier_call_chain(unsigned long val, void *v)  
  45. {  
  46.     return blocking_notifier_call_chain(&fb_notifier_list, val, v);  
  47. }  
  48. EXPORT_SYMBOL_GPL(fb_notifier_call_chain);  

假如framebuffer为A子系统,触屏ft5x06为B子系统,现想要做到触屏伴随显示屏息屏而休眠,亮屏而唤醒。

B子系统(触屏)/被通知者,代码如下:
[cpp] view plain copy
  1. kernel\drivers\input\touchscreen\ft5x06_ts.c   
  2.   
  3. #if defined(CONFIG_FB)  
  4. static int fb_notifier_callback(struct notifier_block *self,  
  5.                  unsigned long event, void *data)  
  6. {  
  7.     struct fb_event *evdata = data;  
  8.     int *blank;  
  9.     struct ft5x06_ts_data *ft5x06_data =  
  10.         container_of(self, struct ft5x06_ts_data, fb_notif);  
  11.                                 /* 检测是否是显示器BLANK改变事件 */  
  12.     if (evdata && evdata->data && event == FB_EVENT_BLANK &&  
  13.             ft5x06_data && ft5x06_data->client) {  
  14.         blank = evdata->data;  
  15.         if (*blank == FB_BLANK_UNBLANK) /*是BLANK事件中的LCD亮屏事件,唤醒触屏*/  
  16.             ft5x06_ts_resume(&ft5x06_data->client->dev);  
  17.         else if (*blank == FB_BLANK_POWERDOWN)  /*是BLANK事件中的LCD灭屏事件,让触屏休眠 */  
  18.             ft5x06_ts_suspend(&ft5x06_data->client->dev);  
  19.     }  
  20.   
  21.     return 0;  
  22. }  
  23. #elif defined(CONFIG_HAS_EARLYSUSPEND)  
  24.   
  25.     //......  
  26.       
  27. #endif  
  28.   
  29. static int ft5x06_ts_probe(struct i2c_client *client,  
  30.                const struct i2c_device_id *id)  
  31. {  
  32.   
  33.     //......  
  34. #if defined(CONFIG_FB)  
  35.     data->fb_notif.notifier_call = fb_notifier_callback;  
  36.     /*注册fb回调函数*/  
  37.     err = fb_register_client(&data->fb_notif);  
  38.   
  39.     if (err)  
  40.         dev_err(&client->dev, "Unable to register fb_notifier: %d\n",  
  41.             err);  
  42. #endif  
  43.     //......  
  44.   
  45. }  
  46. static int __devexit ft5x06_ts_remove(struct i2c_client *client)  
  47. {  
  48.   
  49.     //......  
  50. #if defined(CONFIG_FB)  
  51.     /*注销fb回调函数*/  
  52.     if (fb_unregister_client(&data->fb_notif))  
  53.         dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n");  
  54. #elif defined(CONFIG_HAS_EARLYSUSPEND)  
  55.     unregister_early_suspend(&data->early_suspend);  
  56. #endif  
  57.   
  58.     //......  
  59. }  

A子系统(framebuffer)/通知者,代码如下:
[cpp] view plain copy
  1. int fb_blank(struct fb_info *info, int blank)  
  2. {     
  3.     int ret = -EINVAL;  
  4.   
  5.     if (blank > FB_BLANK_POWERDOWN)  
  6.         blank = FB_BLANK_POWERDOWN;  
  7.   
  8.     if (info->fbops->fb_blank)    /*硬件执行亮屏还是灭屏的操作*/  
  9.         ret = info->fbops->fb_blank(blank, info);  
  10.   
  11.     if (!ret) {  
  12.         struct fb_event event;  
  13.   
  14.         event.info = info;  
  15.         event.data = ␣  
  16.         /*硬件BLANK操作成功后,调用所有的注册回调函数,比如通知给触屏*/  
  17.         fb_notifier_call_chain(FB_EVENT_BLANK, &event);  
  18.     }  
  19.   
  20.     return ret;  
  21. }  
  22.   
  23.     /*fbmem中的ioctl*/  
  24. static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,  
  25.             unsigned long arg)  
  26. {  
  27.   
  28.     //.......  
  29.         case FBIOBLANK: //由上层发下来的亮屏还是息屏的IO命令  
  30.         if (!lock_fb_info(info))  
  31.             return -ENODEV;  
  32.         console_lock();  
  33.         info->flags |= FBINFO_MISC_USEREVENT;  
  34.         ret = fb_blank(info, arg);  
  35.         info->flags &= ~FBINFO_MISC_USEREVENT;  
  36.         console_unlock();  
  37.         unlock_fb_info(info);  
  38.         break;  
  39.     //......  
  40. }  

至于framebuffer中的notifier_call_chain的第二、三个参数,详见linux\fb.h,fbmem.c,fbcon.c。有时间也写个关于fb相关的文章。

通过上面所述,notifier_chain机制只能在内核个子系统间使用,因此,这里使用3个模块:test_notifier_chain_0、test_notifier_chain_1、test_notifier_chain_2;当 test_notifier_chain_2通过module_init初始化模块时发出事件TESTCHAIN_2_INIT;然后 test_notifier_chain_1作出相应的处理:打印 test_notifier_chain_2正在初始化。

[cpp] view plain copy
  1. /* test_chain_0.c :0. 申明一个通知链;1. 向内核注册通知链;2. 定义事件; 3. 导出符号,因而必需最后退出*/  
  2.   
  3. #include <linux/notifier.h>  
  4. #include <linux/module.h>  
  5. #include <linux/init.h>  
  6. #include <linux/kernel.h> /* printk() */  
  7. #include <linux/fs.h> /* everything() */  
  8.   
  9. #define TESTCHAIN_INIT 0x52U  
  10. static RAW_NOTIFIER_HEAD(test_chain);  
  11.   
  12. /* define our own notifier_call_chain */  
  13. static int call_test_notifiers(unsigned long val, void *v)  
  14. {  
  15.     return raw_notifier_call_chain(&test_chain, val, v);  
  16. }  
  17. EXPORT_SYMBOL(call_test_notifiers);  
  18.   
  19. /* define our own notifier_chain_register func */  
  20.  static int register_test_notifier(struct notifier_block *nb)  
  21. {  
  22.     int err;  
  23.     err = raw_notifier_chain_register(&test_chain, nb);  
  24.   
  25.     if(err)  
  26.         goto out;  
  27.   
  28. out:  
  29.     return err;  
  30. }  
  31.   
  32. EXPORT_SYMBOL(register_test_notifier);  
  33.   
  34. static int __init test_chain_0_init(void)  
  35. {  
  36.     printk(KERN_DEBUG "I'm in test_chain_0\n");  
  37.   
  38.     return 0;  
  39. }  
  40.   
  41. static void __exit test_chain_0_exit(void)  
  42. {  
  43.     printk(KERN_DEBUG "Goodbye to test_chain_0\n");  
  44. //  call_test_notifiers(TESTCHAIN_EXIT, (int *)NULL);  
  45. }  
  46.   
  47. MODULE_LICENSE("GPL v2");  
  48. MODULE_AUTHOR("fishOnFly");  
  49.   
  50. module_init(test_chain_0_init);  
  51. module_exit(test_chain_0_exit);  
  52.   
  53. /* test_chain_1.c :1. 定义回调函数;2. 定义notifier_block;3. 向chain_0注册notifier_block;*/  
  54. #include <linux/notifier.h>  
  55. #include <linux/module.h>  
  56. #include <linux/init.h>  
  57.   
  58. #include <linux/kernel.h> /* printk() */  
  59. #include <linux/fs.h> /* everything() */  
  60.   
  61. extern int register_test_notifier(struct notifier_block *nb);  
  62. #define TESTCHAIN_INIT 0x52U  
  63.   
  64. /* realize the notifier_call func */  
  65. int test_init_event(struct notifier_block *nb, unsigned long event,  
  66.     void *v)  
  67. {  
  68.     switch(event){  
  69.     case TESTCHAIN_INIT:  
  70.         printk(KERN_DEBUG "I got the chain event: test_chain_2 is on the way of init\n");  
  71.         break;  
  72.   
  73.     default:  
  74.         break;  
  75.     }  
  76.   
  77.     return NOTIFY_DONE;  
  78. }  
  79. /* define a notifier_block */  
  80. static struct notifier_block test_init_notifier = {  
  81.     .notifier_call = test_init_event,  
  82. };  
  83. static int __init test_chain_1_init(void)  
  84. {  
  85.     printk(KERN_DEBUG "I'm in test_chain_1\n");  
  86.     register_test_notifier(&test_init_notifier);<span style="white-space:pre">  </span>// 由chain_0提供的设施  
  87.     return 0;  
  88. }  
  89.   
  90. static void __exit test_chain_1_exit(void)  
  91. {  
  92.     printk(KERN_DEBUG "Goodbye to test_clain_l\n");  
  93. }  
  94.   
  95. MODULE_LICENSE("GPL");  
  96. MODULE_AUTHOR("fishOnFly");  
  97.   
  98. module_init(test_chain_1_init);  
  99. module_exit(test_chain_1_exit);  
  100.   
  101. /* test_chain_2.c:发出通知链事件*/  
  102.   
  103. #include <linux/notifier.h>  
  104. #include <linux/module.h>  
  105. #include <linux/init.h>  
  106. #include <linux/kernel.h> /* printk() */  
  107. #include <linux/fs.h> /* everything() */  
  108.   
  109. extern int call_test_notifiers(unsigned long val, void *v);  
  110. #define TESTCHAIN_INIT 0x52U  
  111.   
  112. static int __init test_chain_2_init(void)  
  113. {  
  114.     printk(KERN_DEBUG "I'm in test_chain_2\n");  
  115.     call_test_notifiers(TESTCHAIN_INIT, "no_use");  
  116.       
  117.     return 0;  
  118. }  
  119.   
  120. static void __exit test_chain_2_exit(void)  
  121. {  
  122.     printk(KERN_DEBUG "Goodbye to test_chain_2\n");  
  123. }  
  124.   
  125. MODULE_LICENSE("GPL v2");  
  126. MODULE_AUTHOR("fishOnFly");  
  127.   
  128. module_init(test_chain_2_init);  
  129. module_exit(test_chain_2_exit);  
  130.   
  131. # Makefile  
  132.   
  133. # Comment/uncomment the following line to disable/enable debugging  
  134. # DEBUG = y  
  135.   
  136.   
  137. # Add your debugging flag (or not) to CFLAGS  
  138. ifeq ($(DEBUG),y)  
  139.   DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines  
  140. else  
  141.   DEBFLAGS = -O2  
  142. endif  
  143.   
  144.   
  145. ifneq ($(KERNELRELEASE),)  
  146. # call from kernel build system  
  147.   
  148. obj-m   := test_chain_0.o test_chain_1.o test_chain_2.o  
  149.   
  150. else  
  151.   
  152. KERNELDIR ?= /lib/modules/$(shell uname -r)/build  
  153. PWD       := $(shell pwd)  
  154.   
  155. modules:  
  156.     $(MAKE) -C $(KERNELDIR) M=$(PWD) modules  
  157.   
  158. endif  
  159.   
  160.   
  161.   
  162. clean:  
  163.     rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions  
  164.   
  165. depend .depend dep:  
  166.     $(CC) $(CFLAGS) -M *.c > .depend  
  167.   
  168.   
  169. ifeq (.depend,$(wildcard .depend))  
  170. include .depend  
  171. endif  
  172.   
  173. [wang2@iwooing: notifier_chian]$ sudo insmod./test_chain_0.ko  
  174. [wang2@iwooing: notifier_chian]$ sudo insmod./test_chain_1.ko  
  175. [wang2@iwooing: notifier_chian]$ sudo insmod./test_chain_2.ko  
  176.    
  177.   
  178. [wang2@iwooing: notifier_chian]$ dmesg  
  179.   
  180. [ 5950.112649] I'm in test_chain_0  
  181. [ 5956.766610] I'm in test_chain_1  
  182. [ 5962.570003] I'm in test_chain_2  
  183. [ 5962.570008] I got the chain event: test_chain_2 is on the way of init  
  184.   
  185. [ 6464.042975] Goodbye to test_chain_2  
  186. [ 6466.368030] Goodbye to test_clain_l  
  187. [ 6468.371479] Goodbye to test_chain_0  

原创粉丝点击