Linux内核通知链( 三 )

  • 通知者:事件的通知者 。当检测到某事件,或者本身产生事件时,通知所有对该事件感兴趣的一方事件发生 。它定义了一个通知链,其中保存了每一个被通知者对事件的回调函数 。通知这个过程实际上就是遍历通知链中的每一项,然后调用相应的回调函数 。
  • 包括以下过程:
    • 通知者定义通知链 。
    • 被通知者向通知链中注册回调函数 。
    • 当事件发生时,通知者发出通知(执行通知链中所有元素的回调函数) 。
    其他注意事项
    1. 如果一个子系统A在运行过程中会产生一个实时事件,而这些事件对其他子系统来说非常重要,那么子系统A可以定义一个自己的通知链对象,根据需求可以选择原子通知链、非阻塞通知链和原始通知链,并向外提供向这个通知链里注册、卸载、执行事件的回调函数的接口 。
    2. 如果子系统B对子系统A中的某些事件感兴趣,或者说强依赖,就是说子系统B需要根据子系统A中某些事件来执行自己特定的操作,那么此时系统B需要实例化一个通知块 struct notifier_block xxx{} ,然后编写通知块里的回调处理函数来相应系统A中的事件就可以了 。
    3. 通知块 struct notifier_block xxx{} 里有一个优先级的特性,起始在标准内核里每个实例化的通知块都没有使用优先级 。不用优先级字段的结果就是:先注册的通知块里的回调函数在事件发生时会先执行 。注意这里说的后注册指的是模块被动态加载到内核的先后顺序,和哪个模块代码先写没有关系 。
    注意区分 。意思就是说,如果子系统B和C都对子系统A的up事件感兴趣,B和C在向A注册up事件的回调函数时并没有指定函数的优先级 。无论是通过`insmod`手动加载模块B和C,还是系统 boot 时自动加载B和C,哪个模块先被加载,它的回调函数在A系统的up事件发生时会先被执行
    4. 关于通知链的回调函数,正常情况下都需要返回 NOTIFY_OK 或者 NOTIFY_DONE ,这样通知链上后面挂载的其他函数可以继续执行 。如果返回 NOTIFY_STOP ,则会使得通知链上后续挂载的函数无法得到执行,除非特别想这么做,否则编写通知链回调函数时,最好不要返回这个值 。
    5. 通知链上的回调函数的原型为:
    typedef int (*notifier_fn_t)(struct notifier_block *nb, unsigned long action, void *data);struct notifier_block { notifier_fn_t notifier_call; struct notifier_block __rcu *next; int priority;};其中第二个参数一般用于指明事件的类型 。通知都是一个整数;而第三个参数是一个 void 类型的内存地址,在不同的子系统中表示不同的信息 。我们在设计自己的通知链系统可以用第三个入参实现在通知系统和被通知系统之间数据的传递,以便被通知系统的工作可以更加紧凑、高效 。
    6. 如果以后在看到内核代码中某个子系统在调用通知链注册函数时,做到以下几点就没事了:
    • 心里首先要明确,这个注册通知链回调函数的系统一定和提供通知链的系统有某种联系,且本系统需要那个系统对某些重要事件进行响应 。
    • 看本系统注册的通知链回调函数的实现,具体看它对哪些事件感兴趣,并且是怎么处理的 。
    • 看看提供通知链对象的系统有哪些事件;
    最后,也就明白了这个子系统为什么要用通知链来感知别的系统的变化了,这样一来,对这两个子系统从宏观到微观的层面上都有一个总体的认识和把握,后续研究起来就顺风顺水了 。
    了解更多,Linux相关知识,可以关注我 。




    推荐阅读