Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8834
  • 博文数量: 2
  • 博客积分: 26
  • 博客等级: 民兵
  • 技术积分: 20
  • 用 户 组: 普通用户
  • 注册时间: 2012-12-20 12:47
文章分类
文章存档

2016年(2)

我的朋友
最近访客

分类: C/C++

2016-01-28 21:14:05

在阅读内核源码的时候,到处会看到通知链的身影。从技术上来讲,这并不是一个多么复杂、高 深、难懂的部分,说白了就是一个单向链表的插入、删除和遍历等操作。但这部分是由协议栈头号大Boss----Alan Cox亲自主刀,足以说明这个基础特性的重要性,也有很多值得我们学习的地方。内核中通知链的基础文件就两个,头文件include/linux /notifier.h,源文件kernel/notifier.c,头文件和源文件所有代码加起来不超过1000行,总体来说还是比较好懂。
   刚才说过,通知链的原型就是一个单向链表,内核提供的通知链机制主要用于不同子系统之间通信,基于事件和优先级。往通俗里将,考虑这么一种场景:对于网 卡驱动子系统来说,经常会发生的情况就是什么?网卡IP地址有变化,网卡状态有变化等等。那么如果有其他子系统,比如路由子系统最网卡IP地址变化这件事 比较感兴趣,它该怎么去感知这件事儿呢?当然这种场景下,很多人第一直觉就是“订阅者-发布者”模型。不过确实是这样的,通知链机制可以算作是“订阅者- 发布者”模型的一种。每个子系统都会有些一些重要事件,例如前面说的,网络驱动子系统网卡的事件,或者USB的状态事件等等,这些子系统都会提供一个自己 的事件队列,这个队列都是其他函数提供的回调函数。当有事件发生时,子系统就会去遍历其事件队列上已经注册了的所有回调函数,这样就实现了“通知”的目 的。说的云里雾里的,还是看图吧:
   对系统A来说,它自己的通知队列上被被人注册了三个回调函数,那么当系统A的某个事件发生时,它必须去遍历自己的事件队列headA,然后依次去执行队列里每个回调函数(这么说不太准确,不一定每个函数都执行,后面解释)。对子系统B来说,情况是一样地。

   内核里通知链队列里,每个元素都是一个通知块,原型如下:

点击(此处)折叠或打开

  1. /* include/linux/notifier.h*/
  2. struct notifier_block {
  3.     int (*notifier_call)(struct notifier_block *, unsigned long, void *);
  4.     struct notifier_block *next;
  5.     int priority;
  6. };

    notifier_call是回调函数的指针,指向的函数是当事件发生时要执行的函数;next指向下一个回调函数的通知块;priority是事件发生时本函数(由notifier_call所指向)执行的优先级,数字越小优先级越高,越会先被执行。我们看到这个通知块的结构并不复杂,甚至可以说是已经非常简单明了,每一个这样的通知块串起来就是我们所说的通知链了。
   Linux内核提供了三类通知链:原子通知链、阻塞通知链和原始通知链,它们的主要区别就是在执行通知链上的回调函数时是否有安全保护措施。下面我们分别看一下这三类通知链:
    1、原子通知链(Atomic Notifier Chains)
   原子通知链的链表头定义如下:

点击(此处)折叠或打开

  1. struct atomic_notifier_head {
  2.     spinlock_t lock;
  3.     struct notifier_block *head;
  4. };
    我们可以看到原子通知链采用的是自旋锁,通知链元素的回调函数(当事件发生时要执行的函数)只能在中断上下文中运行,而且不允许阻塞。

   2、可阻塞通知链(Blocking Notifier Chains)
   可阻塞的通知链有两种类型,一种用信号量实现回调函数的加锁,另一种是采用互斥锁和叫做“可睡眠的读拷贝更新机制”(Sleepable Read-Copy UpdateSleepable Read-Copy Update),链表头的定义分别如下:   

点击(此处)折叠或打开

  1. struct blocking_notifier_head {
  2.     struct rw_semaphore rwsem;
  3.     struct notifier_block *head;
  4. };

点击(此处)折叠或打开

  1. struct srcu_notifier_head {
  2.     struct mutex mutex;
  3.     struct srcu_struct srcu;
  4.     struct notifier_block *head;
  5. };
    可阻塞型的通知链运行在进程空间的上下文环境里。

   3、原始通知链(Raw Notifier Chains)
   顾名思义,没有任何安保措施,对链表的加锁和保护全部由调用者自己实现,定义如下:

点击(此处)折叠或打开

  1. struct raw_notifier_head {
  2.     struct notifier_block *head;
  3. };
    关于三大类通知链详细的描述在notifier.h文件头部已经有非常详细的描述和说明了,这里我就浪费笔墨了,大家看源代码里的文注释完全足够了

   这三类通知链,我们该怎么用这才是我需要关心的问题。在定义自己的通知链的时候,心里必须明确,自己需要一个什么样类型的通知链,是原子的、可阻塞的还是一个原始通知链。内核中用于定义并初始化不同类通知链的函数分别是:

点击(此处)折叠或打开

  1. ATOMIC_NOTIFIER_HEAD(name)     //定义并初始化一个名为name的原子通知链
  2. BLOCKING_NOTIFIER_HEAD(name)   //定义并初始化一个名为name的阻塞通知链
  3. RAW_NOTIFIER_HEAD(name)        //定义并初始化一个名为name的原始通知链
    其实ATOMIC_NOTIFIER_HEAD(mynotifierlist)和下面的代码是等价的,展开之后如下

点击(此处)折叠或打开

  1. struct atomic_notifier_head mynotifierlist = 
  2. {
  3.     .lock = __SPIN_LOCK_UNLOCKED(mynotifierlist.lock),
  4.     .head = NULL 
  5. }

    另外两个接口也类似。如果我们已经有一个通知链的对象,Linux还提供了一组用于初始化一个通知链对象的API:

点击(此处)折叠或打开

  1. ATOMIC_INIT_NOTIFIER_HEAD(name)
  2. BLOCKING_INIT_NOTIFIER_HEAD(name)
  3. RAW_INIT_NOTIFIER_HEAD(name)

    这一组接口一般在下列格式的代码里见到的会比较多一点:

点击(此处)折叠或打开

  1. static struct atomic_notifier_head dock_notifier_list;
  2. ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
    
   OK,有了通知链只是第一步,接下来我们还需要提供往通知链上注册通知块、卸载通知块、已经遍历执行通知链上每个通知块里回调函数的基本接口,说白了就是单向链表的插入、删除和遍历,这样理解就可以了。
    内核提供最基本的通知链的常用接口如下:

点击(此处)折叠或打开

  1. static int notifier_chain_register(struct notifier_block **nl,  struct notifier_block *n);
  2. static int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n);
  3. static int __kprobes notifier_call_chain(struct notifier_block **nl, unsigned long val, void *v, int nr_to_call, int *nr_calls);

    这最基本的三个接口分别实现了通知链上通知块的注册、卸载和遍历操作,可以想象,原子通知链、可阻塞通知链和原始通知链一定会对基本通知链的操作函数进行一次包装的,事实也确实如此:

点击(此处)折叠或打开

  1. //原子通知链
  2. int atomic_notifier_chain_register(struct atomic_notifier_head *nh,  struct notifier_block *nb);
  3. int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, struct notifier_block *nb);
  4. int atomic_notifier_call_chain(struct atomic_notifier_head *nh, unsigned long val, void *v);

  5. //可阻塞通知链
  6. int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
  7. int blocking_notifier_chain_cond_register(struct blocking_notifier_head *nh, struct notifier_block *nb);
  8. int srcu_notifier_chain_register(struct srcu_notifier_head *nh, struct notifier_block *nb);

  9. int blocking_notifier_call_chain(struct blocking_notifier_head *nh,unsigned long val, void *v);
  10. int srcu_notifier_call_chain(struct srcu_notifier_head *nh, unsigned long val, void *v);

  11. int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *nb);
  12. int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh, struct notifier_block *nb);

  13. //原始通知链
  14. int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *nb);
  15. int raw_notifier_chain_unregister(struct raw_notifier_head *nh,struct notifier_block *nb);
  16. int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v);

    上述这三类通知链的基本API又构成了内核中其他子系统定义、操作自己通知链的基础。例如,Netlink定义了一个原子通知链,所以,它对原子通知链的基本API又封装了一层,以形成自己的特色

点击(此处)折叠或打开

  1. /*net/netlink/af_netlink.c*/
  2. ...
  3. static ATOMIC_NOTIFIER_HEAD(netlink_chain);
  4. ...
  5. int netlink_register_notifier(struct notifier_block *nb)
  6. {
  7.     return atomic_notifier_chain_register(&netlink_chain, nb);
  8. }
  9. ...

  10. int netlink_unregister_notifier(struct notifier_block *nb)
  11. {
  12.     return atomic_notifier_chain_unregister(&netlink_chain, nb);
  13. }
  14. ...

    网络事件也有一个原子通知链:

点击(此处)折叠或打开

  1. /*net/core/netevent.c*/
  2. /*
  3.  *    Network event notifiers
  4.  *
  5.  *    Authors:
  6.  * Tom Tucker <tom@opengridcomputing.com>
  7.  * Steve Wise <swise@opengridcomputing.com>
  8.  *
  9.  *    This program is free software; you can redistribute it and/or
  10.  * modify it under the terms of the GNU General Public License
  11.  * as published by the Free Software Foundation; either version
  12.  * 2 of the License, or (at your option) any later version.
  13.  *
  14.  *    Fixes:
  15.  */

  16. #include <linux/rtnetlink.h>
  17. #include <linux/notifier.h>
  18. #include <net/netevent.h>

  19. static ATOMIC_NOTIFIER_HEAD(netevent_notif_chain);

  20. /**
  21.  *    register_netevent_notifier - register a netevent notifier block
  22.  *    @nb: notifier
  23.  *
  24.  *    Register a notifier to be called when a netevent occurs.
  25.  *    The notifier passed is linked into the kernel structures and must
  26.  *    not be reused until it has been unregistered. A negative errno code
  27.  *    is returned on a failure.
  28.  */
  29. int register_netevent_notifier(struct notifier_block *nb)
  30. {
  31.     int err;

  32.     err = atomic_notifier_chain_register(&netevent_notif_chain, nb);
  33.     return err;
  34. }

  35. /**
  36.  *    netevent_unregister_notifier - unregister a netevent notifier block
  37.  *    @nb: notifier
  38.  *
  39.  *    Unregister a notifier previously registered by
  40.  *    register_neigh_notifier(). The notifier is unlinked into the
  41.  *    kernel structures and may then be reused. A negative errno code
  42.  *    is returned on a failure.
  43.  */

  44. int unregister_netevent_notifier(struct notifier_block *nb)
  45. {
  46.     return atomic_notifier_chain_unregister(&netevent_notif_chain, nb);
  47. }

  48. /**
  49.  *    call_netevent_notifiers - call all netevent notifier blocks
  50.  * @val: value passed unmodified to notifier function
  51.  * @v: pointer passed unmodified to notifier function
  52.  *
  53.  *    Call all neighbour notifier blocks. Parameters and return value
  54.  *    are as for notifier_call_chain().
  55.  */

  56. int call_netevent_notifiers(unsigned long val, void *v)
  57. {
  58.     return atomic_notifier_call_chain(&netevent_notif_chain, val, v);
  59. }

  60. EXPORT_SYMBOL_GPL(register_netevent_notifier);
  61. EXPORT_SYMBOL_GPL(unregister_netevent_notifier);
  62. EXPORT_SYMBOL_GPL(call_netevent_notifiers)
   可阻塞通知链里的SRCU通知链,由于使用条件较苛刻,限制条件较多,所以使用的机会不是很多,除非你特别清楚这种类型的通知链的适用场合,在2.6.32的内核里只有cpufreq.c在用这种类型的通知链。
关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不 同子系统之间的通信,通知链只能用于内核不同子系统之间的通信。那么内核通知链到底是怎么工作的?我们如何才能用好通知链?内核源代码里随处可见的通知链 身影,我们到底该如何理解呢?本片博文过后,您的这些疑问和顾虑将统统消除。

   以前有个女神,超凡脱俗、出水芙蓉,不过在怎么滴也是人,是人就会有各种各样的需求,女神的所有需求都放在她的需求链表里requirment_chain,比如物质需求,精神需求等等。然后女神首先需要做的事情就是将自己的需求链给实例化了:

点击(此处)折叠或打开

  1. /* Godness.c */
  2. /* 我们假设女神需求链的类型是原始通知链(PS:不要和原始需求挂钩理解 -_-||)*/
  3. static RAW_NOTIFIER_HEAD(requirment_chain);


    当需求被定义出来后,还需要向外提供两个接口:一个是别人用于满足她需求的接口,另一个是别人需要和她break out的接口(虽然在现实生活中这种情况比较令人sadness,但女神所在的虚拟世界里这个是必须的)。于是女神提供了别人往其需求链注册响应函数的接 口和卸载响应函数的接口:

点击(此处)折叠或打开

  1. /* Godness.c*/

  2. int register_godness_notifier(struct notifier_block *nb)
  3. {
  4.         return raw_notifier_chain_register(&requirment_chain, nb);
  5. }
  6. EXPORT_SYMBOL(register_godness_notifier); //注册函数实现了之后必须将其公布出去,不然别人怎么看得到呢

  7. int unregister_godness_notifier(struct notifier_block *nb)
  8. {
  9.         return raw_notifier_chain_unregister(&requirment_chain, nb);
  10. }
  11. EXPORT_SYMBOL(unregister_godness_notifier); //同上

    然后,女神要做的就是提需求,并看看哪些屌丝、土豪或高富帅来追求自己:

点击(此处)折叠或打开

  1. int call_godness_notifier_chain(unsigned long val, void *v)
  2. {
  3.         return raw_notifier_call_chain(&requirment_chain, val, v);
  4. }
  5. EXPORT_SYMBOL(call_godness_notifier_chain);

    为了模拟测试过程,我们需要一个内核线程,模拟女神提需求的过程,然后不断调用上面的需求响应的检测函数。我们姑且认为认为女神的需求有两种:物质需求就是对menoy的需求,精神需求就是音乐的需求。女神每3秒钟提一个需求,一共提10个需求:

点击(此处)折叠或打开

  1. #define PHY_REQ 0 //物质需求
  2. #define SPR_REQ 1 //精神需求

  3. #define REQ_MAX SPR_REQ+1

  4. static int make_requirment_thread(void *data)
  5. {
  6.      int i = 10;
  7.      struct completion cmpl;
  8.      unsigned int requirment_type = 0;
  9.      printk("[Godness]requirements thread starting...\n");
  10.      while((i--) > 0){
  11.             init_completion(&cmpl);
  12.             wait_for_completion_timeout(&cmpl, 3 * HZ);

  13.             get_random_bytes(&requirment_type,sizeof(requirment_type));  //生成一个内核随机数
  14.             requirment_type %= REQ_MAX;  //需求类型之可能是0或者1

  15.             printk("[Godness]requirment type: %d \n",requirment_type);
  16.             call_godness_notifier_chain(requirment_type,NULL);
  17.      }
  18.      printk("[Godness]requirements thread ended!\n");
  19.      return 0;
  20. }

    女神的最终模型如下:

点击(此处)折叠或打开

  1. #include <asm/uaccess.h>
  2. #include <linux/types.h>
  3. #include <linux/kernel.h>
  4. #include <linux/sched.h>
  5. #include <linux/notifier.h>
  6. #include <linux/init.h>
  7. #include <linux/types.h>
  8. #include <linux/module.h>
  9. #include <linux/kthread.h>
  10. MODULE_LICENSE("GPL");

  11. #define PHY_REQ 0 //物质需求
  12. #define SPR_REQ 1 //精神需求
  13. #define REQ_MAX SPR_REQ+1

  14. extern void get_random_bytes(void* buf,int nbytes);
  15. static struct task_struct *requirments_thread = NULL;
  16. /*
  17. * 女神所有的需求都会列在她的需求链里。这里我们定义了一个原始通知链,暂时没考虑锁的问题。
  18. */
  19. static RAW_NOTIFIER_HEAD(requirment_chain);

  20. /*
  21. * 如果谁想追求本女王,就来献殷勤吧
  22. */
  23. int register_godness_notifier(struct notifier_block *nb)
  24. {
  25.         return raw_notifier_chain_register(&requirment_chain, nb);
  26. }
  27. EXPORT_SYMBOL(register_godness_notifier);

  28. /*
  29. * 伺候不起的,赶紧Get out as soon as
  30. */
  31. int unregister_godness_notifier(struct notifier_block *nb)
  32. {
  33.         return raw_notifier_chain_unregister(&requirment_chain, nb);
  34. }
  35. EXPORT_SYMBOL(unregister_godness_notifier);

  36. /*
  37. * 本女王开始提需求了,看看谁能才是真心的。
  38. */
  39. int call_godness_notifier_chain(unsigned long val, void *v)
  40. {
  41.         return raw_notifier_call_chain(&requirment_chain, val, v);
  42. }
  43. EXPORT_SYMBOL(call_godness_notifier_chain);

  44. static int make_requirment_thread(void *data)
  45. {
  46.      int i = 10;
  47.      struct completion cmpl;
  48.      unsigned int requirment_type = 0;
  49.      printk("[Godness]requirements thread starting...\n");
  50.      while((i--) > 0){
  51.             init_completion(&cmpl);
  52.             wait_for_completion_timeout(&cmpl, 3 * HZ);
  53.             get_random_bytes(&requirment_type,sizeof(requirment_type));  //生成一个内核随机数
  54.             requirment_type %= REQ_MAX;  //需求类型之可能是0或者1
  55.            printk("[Godness]requirment type: %d \n",requirment_type);
  56.             call_godness_notifier_chain(requirment_type,NULL);
  57.      }
  58.      printk("[Godness]requirements thread ended!\n");
  59.      return 0;
  60. }

  61. static int __init godness_init_notifier(void)
  62. {
  63.         printk("[Attention]The Godness coming into the world!\n");
  64.         requirments_thread = kthread_run(make_requirment_thread,NULL,"Godness_requirments_thread");
  65.         return 0;
  66. }

  67. static void __exit godness_exit_notifier(void)
  68. {
  69.         printk("[Attention]The Godness leaving out!\n");
  70. }
  71. module_init(godness_init_notifier);
  72. module_exit(godness_exit_notifier);

    这个时候有个叫土豪的家伙,突然于茫茫人海中发现了女神,并且知道了女神有金钱需求的欲望,于是土豪向女神的需求链里注册了一个金钱的响应函数,这样一旦女神需要用钱的时候他第一时间就能收到通知,然后以迅雷下载不及掩耳盗铃之势加以满足:

点击(此处)折叠或打开

  1. /*Tuhao.c*/

  2. extern int register_godness_notifier(struct notifier_block*);
  3. extern int unregister_godness_notifier(struct notifier_block*);

  4. static int baby_need_money(struct notifier_block *this, unsigned long event, void *ptr)
  5. {
  6.         if(event != 0)  //不是金钱需求关我鸟事
  7.         {
  8.             return NOTIFY_DONE; //Don't care
  9.         }
  10.         printk("[Tuhao]Hi Baby,$$$$$$$$ 么么哒 \n");
  11.         return NOTIFY_OK;
  12. }

  13. static struct notifier_block cash_notifier =
  14. {
  15.         .notifier_call = baby_need_money,
  16.         .priority = 2,
  17. };

  18. static int __init tuhao_register(void)
  19. {
  20.         int err;
  21.         printk("[Tuhao]Tuhao register cash_requirment response to Godness...");

  22.         err = register_godness_notifier(&cash_notifier);
  23.         if (err)
  24.         {
  25.                 printk("Refused!\n");
  26.                 return -1;
  27.         }
  28.         printk("Accepted!\n");

  29.         return err;
  30. }

  31. static void __exit tuhao_unregister(void)
  32. {
  33.         unregister_godness_notifier(&cash_notifier);
  34.         printk("[Tuhao]Tuhao is giving up Godness!(Son of bitch)\n");
  35. }

  36. module_init(tuhao_register);
  37. module_exit(tuhao_unregister);


   这时,有一个屌丝,也于茫茫人海中发现了女神,他发现女神喜欢音乐,于是他开始响应女神的精神需求:

点击(此处)折叠或打开

  1. /*Diors.c*/

  2. extern int register_godness_notifier(struct notifier_block*);
  3. extern int unregister_godness_notifier(struct notifier_block*);

  4. static int godness_need_music(struct notifier_block *this, unsigned long event, void *ptr)
  5. {
  6.         if(event != 1) //我又没钱,给不了你大房子、气派的车子...
  7.         {
  8.             return NOTIFY_DONE; //Don't care
  9.         }
  10.         printk("[Diors]Hi girl,This is a classic Music disk,take it. \n");
  11.         return NOTIFY_OK;
  12. }

  13. static struct notifier_block music_notifier =
  14. {
  15.         .notifier_call = godness_need_music,
  16.         .priority = 2,
  17. };

  18. static int __init diors_register(void)
  19. {
  20.         int err;
  21.         printk("[Diors]Diors register music_requirment response to Godness...");

  22.         err = register_godness_notifier(&music_notifier);
  23.         if (err)
  24.         {
  25.                 printk("Refused!\n");
  26.                 return -1;
  27.         }
  28.         printk("Accepted!\n");

  29.         return err;
  30. }

  31. static void __exit diors_unregister(void)
  32. {
  33.         unregister_godness_notifier(&music_notifier);
  34.         printk("[Diors]Tuhao is giving up Godness!(What a pity)\n");
  35. }

  36. module_init(diors_register);
  37. module_exit(diors_unregister);


    好的,到此为止,一切就绪,好戏正式开始:

点击(此处)折叠或打开

  1. #Makefile for fun

  2. obj-m:=Goddess.o Tuhao.o Diors.o

  3. CURRENT_PATH := $(shell pwd)
  4. KERNEL_VERSION := $(shell uname -r)
  5. KERNEL_HEADER_DIR := /usr/src/kernels/$(LINUX_KERNELKERNEL_VERSION)
  6. all:
  7.         make -C $(KERNEL_HEADER_DIR) M=$(CURRENT_PATH) modules
  8. clean:
  9.         make -C $(KERNEL_HEADER_DIR) M=$(CURRENT_PATH) clean

   主角们闪亮登场:

   It's time time to show :)

   我们可以看到,女神初到人间时需要用钱,结果没人搭理,去了趟韩国回来之后,被土豪给瞄到了,于是土豪开始大把大把地视金钱如粪土。过了8秒钟,屌丝男也发现了女神,当女神想听歌时屌丝男就屁颠屁颠地把自己珍藏了多年的古典乐光盘送给了女神。最后剧中,谢幕。

   OK,让我们总结一下Linux内核通知链的应用场景。如果一个子系统需要向外通告事件时,它需要首先定义自己的通知链对象,然后向内核里其他子系统提供一个向自己的通知链注册消息响应函数的接口,当然也必须提供一个用于从自己从自己的通知链上卸载响应函数的接口。接下来,我们这个子系统要做的事情就是根据自己的实际运行情况,定期地产生一些消息,并调用自己通知链里别的系统已经注册好了消息响应函数,这样别的子系统就可以根据我们这个系统的的消息类型进行一些处理动作。
   
那么多个子系统对我们的同一种消息都挂有响应函数时该怎么处理?鉴于时间关系,下次再叙。
话说,女神无论是在土豪或者屌丝那里都找不到归属感,冥冥之中天上掉下来一个王子(PS:又名高富帅),既可以满足女神的物质需求还可以满足女神的精神需求:

点击(此处)折叠或打开

  1. /*GFS.c*/
  2. #include <asm/uaccess.h>
  3. #include <linux/types.h>
  4. #include <linux/kernel.h>
  5. #include <linux/sched.h>
  6. #include <linux/notifier.h>
  7. #include <linux/init.h>
  8. #include <linux/types.h>
  9. #include <linux/module.h>
  10. MODULE_LICENSE("GPL");

  11. /*
  12. * 注册通知链
  13. */

  14. extern int register_godness_notifier(struct notifier_block*);
  15. extern int unregister_godness_notifier(struct notifier_block*);

  16. static int sweet_heart_requirments(struct notifier_block *this, unsigned long event, void *ptr)
  17. {
  18.         switch(event)
  19.         {
  20.                 case 0:
  21.                         printk("[GFS]Hi honey,the VISA card is ready for you! \n");
  22.                         break;
  23.                 case 1:
  24.                         printk("[GFS]Hi honey,let me play the piano for you! \n");
  25.                         break;
  26.                 default:
  27.                         break;
  28.         }
  29.         return 0;
  30. }

  31. static struct notifier_block honey_notifier =
  32. {
  33.         .notifier_call = sweet_heart_requirments,
  34.         .priority = 2,
  35. };

  36. static int __init GFS_register(void)
  37. {
  38.         int err;
  39.         printk("[GFS]GFS register honey_requirment response to Godness...");

  40.         err = register_godness_notifier(&honey_notifier);
  41.         if (err)
  42.         {
  43.                 printk("Refused!\n");
  44.                 return -1;
  45.         }
  46.         printk("Accepted!\n");

  47.         return err;
  48. }

  49. /*
  50. * 卸载刚刚注册了的通知链
  51. */
  52. static void __exit GFS_unregister(void)
  53. {
  54.         unregister_godness_notifier(&honey_notifier);
  55.         printk("[GFS]GFS broke up with Godness!(How sadness)\n");
  56. }
  57. module_init(GFS_register);
  58. module_exit(GFS_unregister);

   然后重新上演前面的故事,
播放的指令如下:
[root@localhost test]# insmod Goddess.ko;sleep 4;insmod Tuhao.ko; sleep 4; insmod Diors.ko;sleep 4; insmod GFS.ko
[root@localhost test]# rmmod GFS.ko;rmmod Diors.ko;rmmod Tuhao.ko;rmmod Goddess.ko
[root@localhost test]#

   播放内容如下:
Jun  2 00:39:41 localhost kernel: [Attention]The Godness coming into the world!
Jun  2 00:39:41 localhost kernel: [Godness]requirements thread starting...
Jun  2 00:39:44 localhost kernel: [Godness]requirment type: 1
Jun  2 00:39:45 localhost kernel: [Tuhao]Tuhao register Cash event  to Godness...Accepted!
Jun  2 00:39:47 localhost kernel: [Godness]requirment type: 0
Jun  2 00:39:47 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:39:49 localhost kernel: [Diors]Diors register music_requirment response to Godness...Accepted!
Jun  2 00:39:50 localhost kernel: [Godness]requirment type: 1
Jun  2 00:39:50 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:39:53 localhost kernel: [Godness]requirment type: 1
Jun  2 00:39:53 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:39:53 localhost kernel: [GFS]GFS register honey_requirment response to Godness...Accepted!
Jun  2 00:39:56 localhost kernel: [Godness]requirment type: 1
Jun  2 00:39:56 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:39:56 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:39:59 localhost kernel: [Godness]requirment type: 0
Jun  2 00:39:59 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:39:59 localhost kernel: [GFS]Hi honey,the VISA card is ready for you!
Jun  2 00:40:02 localhost kernel: [Godness]requirment type: 1
Jun  2 00:40:02 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:40:02 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:40:05 localhost kernel: [Godness]requirment type: 1
Jun  2 00:40:05 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:40:05 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:40:08 localhost kernel: [Godness]requirment type: 0
Jun  2 00:40:08 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:40:08 localhost kernel: [GFS]Hi honey,the VISA card is ready for you!
Jun  2 00:40:11 localhost kernel: [Godness]requirment type: 1
Jun  2 00:40:11 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:40:11 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:40:11 localhost kernel: [Godness]requirements thread ended!
Jun  2 00:40:52 localhost kernel: [GFS]GFS broke up with Godness!(How sadness)
Jun  2 00:40:52 localhost kernel: [Diors]Tuhao is giving up Godness!(What a pity)
Jun  2 00:40:52 localhost kernel: [Tuhao]Tuhao is giving up Godness!(Son of bitch)
Jun  2 00:40:52 localhost kernel: [Attention] The Godness leaving out!

   我们可以看到,高富帅向女神需求通知链上注册的回调函数,优先级也是2,我们在测试的时候是先加载土豪、再加载屌丝最后加载高富帅,所以每当女神提出需 求时,如果是金钱需求则最先被土豪响应,如果是精神需求则最先被屌丝给响应了。这完全不符合高富帅的特征啊,于是高富帅将自己的优先级提高到5,然后 replay一次:

点击(此处)折叠或打开

  1. static struct notifier_block honey_notifier =
  2. {
  3.         .notifier_call = sweet_heart_requirments,
  4.         .priority = 5,
  5. };

    结果如下:
Jun  2 00:50:29 localhost kernel: [Attention]The Godness coming into the world!
Jun  2 00:50:29 localhost kernel: [Godness]requirements thread starting...
Jun  2 00:50:32 localhost kernel: [Godness]requirment type: 0
Jun  2 00:50:33 localhost kernel: [Tuhao]Tuhao register Cash event  to Godness...Accepted!
Jun  2 00:50:35 localhost kernel: [Godness]requirment type: 0
Jun  2 00:50:35 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:50:37 localhost kernel: [Diors]Diors register music_requirment response to Godness...Accepted!
Jun  2 00:50:38 localhost kernel: [Godness]requirment type: 1
Jun  2 00:50:38 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:50:41 localhost kernel: [Godness]requirment type: 0
Jun  2 00:50:41 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:50:41 localhost kernel: [GFS]GFS register honey_requirment response to Godness...Accepted!
Jun  2 00:50:44 localhost kernel: [Godness]requirment type: 0
Jun  2 00:50:44 localhost kernel: [GFS]Hi honey,the VISA card is ready for you!
Jun  2 00:50:44 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:50:47 localhost kernel: [Godness]requirment type: 1
Jun  2 00:50:47 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:50:47 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:50:50 localhost kernel: [Godness]requirment type: 1
Jun  2 00:50:50 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:50:50 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:50:53 localhost kernel: [Godness]requirment type: 0
Jun  2 00:50:53 localhost kernel: [GFS]Hi honey,the VISA card is ready for you!
Jun  2 00:50:53 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:50:56 localhost kernel: [Godness]requirment type: 0
Jun  2 00:50:56 localhost kernel: [GFS]Hi honey,the VISA card is ready for you!
Jun  2 00:50:56 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:50:59 localhost kernel: [Godness]requirment type: 1
Jun  2 00:50:59 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:50:59 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:50:59 localhost kernel: [Godness]requirements thread ended!
Jun  2 00:51:04 localhost kernel: [GFS]GFS broke up with Godness!(How sadness)
Jun  2 00:51:04 localhost kernel: [Diors]Tuhao is giving up Godness!(What a pity)
Jun  2 00:51:04 localhost kernel: [Tuhao]Tuhao is giving up Godness!(Son of bitch)
Jun  2 00:51:04 localhost kernel: [Attention] The Godness leaving out!

    这次我们看到,高富帅终于如愿以偿地于第一时间响应到了女神的需求。再考虑一种情况,如果高富帅不想让女神的需求传递到土豪和屌丝那里,怎么办?其实也很简单,高富帅只要在自己的响应函数里向女神返回NOTIFY_STOP值就可以了:

点击(此处)折叠或打开

  1. static int sweet_heart_requirments(struct notifier_block *this, unsigned long event, void *ptr)
  2. {
  3.         switch(event)
  4.         {
  5.                 case 0:
  6.                         printk("[GFS]Hi honey,the VISA card is ready for you! \n");
  7.                         break;
  8.                 case 1:
  9.                         printk("[GFS]Hi honey,let me play the piano for you! \n");
  10.                         break;
  11.                 default:
  12.                         break;
  13.         }
  14.         return NOTIFY_STOP;
  15. }

   然后故事就变成酱紫了:
Jun  2 00:55:56 localhost kernel: [Attention]The Godness coming into the world!
Jun  2 00:55:56 localhost kernel: [Godness]requirements thread starting...
Jun  2 00:55:59 localhost kernel: [Godness]requirment type: 1
Jun  2 00:56:00 localhost kernel: [Tuhao]Tuhao register Cash event  to Godness...Accepted!
Jun  2 00:56:02 localhost kernel: [Godness]requirment type: 0
Jun  2 00:56:02 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:56:04 localhost kernel: [Diors]Diors register music_requirment response to Godness...Accepted!
Jun  2 00:56:05 localhost kernel: [Godness]requirment type: 1
Jun  2 00:56:05 localhost kernel: [Diors]Hi girl,This is a classic Music disk,take it.
Jun  2 00:56:08 localhost kernel: [Godness]requirment type: 0
Jun  2 00:56:08 localhost kernel: [Tuhao]Hi Baby,$$$$$$$$ 么么哒
Jun  2 00:56:08 localhost kernel: [GFS]GFS register honey_requirment response to Godness...Accepted!
Jun  2 00:56:11 localhost kernel: [Godness]requirment type: 1
Jun  2 00:56:11 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:56:14 localhost kernel: [Godness]requirment type: 0
Jun  2 00:56:14 localhost kernel: [GFS]Hi honey,the VISA card is ready for you!
Jun  2 00:56:17 localhost kernel: [Godness]requirment type: 1
Jun  2 00:56:17 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:56:20 localhost kernel: [Godness]requirment type: 1
Jun  2 00:56:20 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:56:23 localhost kernel: [Godness]requirment type: 0
Jun  2 00:56:23 localhost kernel: [GFS]Hi honey,the VISA card is ready for you!
Jun  2 00:56:26 localhost kernel: [Godness]requirment type: 1
Jun  2 00:56:26 localhost kernel: [GFS]Hi honey,let me play the piano for you!
Jun  2 00:56:26 localhost kernel: [Godness]requirements thread ended!
Jun  2 00:56:30 localhost kernel: [GFS]GFS broke up with Godness!(How sadness)
Jun  2 00:56:30 localhost kernel: [Diors]Tuhao is giving up Godness!(What a pity)
Jun  2 00:56:30 localhost kernel: [Tuhao]Tuhao is giving up Godness!(Son of bitch)
Jun  2 00:56:30 localhost kernel: [Attention] The Godness leaving out!

   在高富帅还没将自己的响应函数注册到女神之前,女神的物质需求是被土豪给霸占了,精 神需求被屌丝给享有了,等到00:56:08秒之后,高富帅的请求被女神接受了,于是接下来就没土豪和屌丝啥事儿了。OK,到这里我们的故事也就讲完了, 让我们回头来总结一下Linux内核中通知链的相关知识点(首先非常感谢wwx0715兄弟指出了我【上】篇博文的一个错误,在【上】篇博文里我将通知链的优先级弄反了,是数字越大优先级越高,通知函数越是先会被执行,在此非常感谢。):
   1、如果一个子系统A在运行过程中会产生一个实时的事件,而这些事件对其他子系统来说非常重要,那么系统A可以定义一个自己的通知链对象,根据需求可以选择原子通知链、非阻塞通知链或者原始通知链,并向外提供向这个通知链里注册、卸载执行事件的回调函数的接口;
   2、如果子系统B对子系统A中的某(些)个事件感兴趣,或者说强依赖,就是说系统B需要根据系统A中某些事件来执行自己特定的操作,那么此时系统就需要实例化一个通知块struct notifier_block XXX{},然后编写通知块里的回调处理函数来响应A系统中响应的事件就可以了;
   3、在我们这个示例里用到了struct notifier_block{}的优先级特性,其实在标准内核里每个实例化的通知块都没有用优先级字段。不用优先级字段的结果就是,先注册的通知块里的 回调函数在事件发生时会先执行。注意这里所说的后注册指的是模块被动态加载内核的先后顺序,和哪个模块的代码先写完没有关系,注意区分。意思就是说,如果 子系统B和C都对子系统A的up事件感兴趣,B和C在向A注册up事件的回调函数时并没有指定函数的优先级。无论是通过insmod手动加载模块B和C, 还是系统boot时自动加载B和C,哪个模块先被加载,它的回调函数在A系统的up事件发生时会先被执行;
   4、关于通知链的回调函数,正常情况下都需要返回NOTIFY_OK或者NOTIFY_DONE,这样通知链上后面挂载的其他函数可以继续执行。如果返回NOTIFY_STOP,则会使得本通知链上后续挂载的函数无法得到执行,除非你特别想这么做,否则在编写通知链的回调函数时一般最好不要返回这个值;
   5、通知链上回调函数的原型int (*notifier_call)(struct notifier_block *, unsigned long, void *),其中第二个参数一般用于指明事件的类型,通常都是一个整数。而第三个参数是一个void类型的内存地址,在不同的子系统中用于表示不同的信息,例如 在邻居子系统里,第三个参数可以是一个邻居信息结构体对象的地址struct neighbour *v;而在驱动框架里又可以是一个net_device{}结构体对象的地址等等;我们在设计自己的通知链系统可以用第三个入参实现在通知系统与被通知系统之间实现数据的传递,以便被通知系统的工作可以更加紧凑、高效;
   6、如果以后再看到内核中某个子系统在调用通知链的注册函数时,不要心虚、不要怕。做到以下两点就没事儿了:
       第一:心里首先要明确,这个注册通知链回调函数的系统一定和提供通知链的系统有某种连续,且本系统需要对那个系统的某些重要事件进行响应;
       第二:看本系统注册的通知回调函数的实现,具体看它对那些事件感兴趣,并且是怎么处理的;
       第三:看看原提供通知链对象的系统有哪些事件;
   最后,心里也就明白了这个系统为什么要用通知链来感知别的系统的变化了,这样一来,对这两个系统从宏观到甚微观的层面就都有一个总体的认识和把握,后续再研究起来就顺风顺水了,而这也正是内核通知链的神奇所在。
   完
阅读(1249) | 评论(0) | 转发(0) |
0

上一篇:Linux内核【链表】整理笔记

下一篇:没有了

给主人留下些什么吧!~~