当前位置: 代码迷 >> 综合 >> Linux内核事件通知链(Linux Notifier Chains)简介
  详细解决方案

Linux内核事件通知链(Linux Notifier Chains)简介

热度:62   发布时间:2023-11-27 14:10:21.0

Linux内核事件通知链(Linux Notifier Chains)简介

一、简单介绍

            Linux内核中大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。
        通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。其实和系统调用signal的思想差不多。

二、Linux内核通知链框架图



三、内核四种通知链类型

   

         1. 原子通知链( Atomic notifier chains ):通知链元素的回调函数(当事件发生时要执行的函数)在中断或原子操作上下文中运行,不允许阻塞。对应的链表头结构:

struct atomic_notifier_head {
        spinlock_t  lock;
        struct  notifier_block *head;
};

         2. 可阻塞通知链( Blocking notifier chains ):通知链元素的回调函数在进程上下文中运行,允许阻塞。对应的链表头:

struct  blocking_notifier_head {
        struct  rw_semaphore  rwsem;
        struct  notifier_block  *head;
};

         3. 原始通知链( Raw notifierchains ):对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护。对应的链表头:

网络子系统就是该类型,通过以下宏实现head的初始化

static RAW_NOTIFIER_HEAD(netdev_chain);
#define RAW_NOTIFIER_INIT(name)    {      \
              .head= NULL }
#define RAW_NOTIFIER_HEAD(name)        \      
struct raw_notifier_head name =        \
                    RAW_NOTIFIER_INIT(name)  
即:
struct raw_notifier_head netdev_chain = {
          .head = NULL;
}

而其回调函数的注册,比如向netdev_chain的注册函数:register_netdevice_notifier。

struct  raw_notifier_head {
        struct  notifier_block  *head;
};

         5. SRCU 通知链( SRCU notifier chains ):可阻塞通知链的一种变体。对应的链表头:

struct  srcu_notifier_head {
        struct  mutex mutex;
        struct  srcu_struct  srcu;
        struct  notifier_block  *head;
};

五、大家应该注意到上面提到的四种通知链里面都含有一个notifier_block结构,在头文件include/linux/notifier.h里面定义。



          notifier_block的数据结构的内容:

                 struct notifier_block {
                        int (*notifier_call)(struct notifier_block *, unsigned long, void *);
                       struct notifier_block __rcu *next;
                       int priority;
                  };

          简单分析一下notifier_block的数据结构:

                1.     notifier_call:当相应事件发生时应该调用的函数,由被通知方提供,如other_subsys_1;

                2.     notifier_block *next:用于链接成链表的指针;

                3.     priority:回调函数的优先级,一般默认为0。


六、运行机制介绍:

           被通知一方(other_subsys_x)通过notifier_chain_register向特定的chain注册回调函数,并且一般而言特定的子系统会用特定的notifier_chain_register包装函数来注册,比如路由子系统使用的是网络子系统的:register_netdevice_notifier来注册他的notifier_block。


七、向事件通知链注册的步骤

           1. 申明struct notifier_block结构

           2. 编写notifier_call函数

           3. 调用特定的事件通知链的注册函数,将notifier_block注册到通知链中

           如果内核组件需要处理够某个事件通知链上发出的事件通知,其就该在初始化时在该通知链上注册回调函数。


八、通知子系统有事件发生

            inet_subsys是通过notifier_call_chain来通知其他的子系统(other_subsys_x)的。

notifier_call_chain会按照通知链上各成员的优先级顺序执行回调函数(notifier_call_x);回调函数的执行现场在notifier_call_chain进程地址空间。



九、实例测试原始通知链,测试代码如下:


1、notifier_chain_register.c(注:申明一个通知链, 向内核注册通知链, 定义事件; 

  

#include <linux/notifier.h>  
#include <linux/module.h>  
#include <linux/init.h>  
#include <linux/kernel.h> 
#include <linux/fs.h>   static RAW_NOTIFIER_HEAD(notfier_chain_fp);  static int invoke_notifiers_chain(unsigned long val, void *v)  
{  printk(KERN_DEBUG "invoke_notifiers_chain\n"); return raw_notifier_call_chain(?fier_chain_fp, val, v);  
}  
EXPORT_SYMBOL(invoke_notifiers_chain);  static int register_notifier_chain(struct notifier_block *nb)  
{  int err;  err = raw_notifier_chain_register(?fier_chain_fp, nb);  if(err)  {printk(KERN_DEBUG "raw_notifier_chain_register error!\n"); goto out;  }elseprintk(KERN_DEBUG "raw_notifier_chain_register success!\n");out:  return err;  
}  EXPORT_SYMBOL(register_notifier_chain);  static int __init notifier_chain_init(void)  
{  printk(KERN_DEBUG "notifier_chain_init\n");  return 0;  
}  static void __exit notifier_chain_exit(void)  
{  printk(KERN_DEBUG "notifier_chain_exit\n");  
}  MODULE_LICENSE("GPL v2");  
MODULE_AUTHOR("weifanghai");  module_init(notifier_chain_init);  
module_exit(notifier_chain_exit);

对应的Makefile

obj-m +=notifier_chain_register.o KDIR := /home/weifanghai/Android_4.4_git/xunwei/kernel/iTop4412_Kernel_3.0
PWD ?= $(shell pwd)all:make -C $(KDIR) M=$(PWD) modulesclean:rm -rf *.o

2、receiver_notifier_chain.c(注: 定义回调函数,定义notifier_block,向tifier_chain_register注册notifier_block


#include <linux/notifier.h>  
#include <linux/module.h>  
#include <linux/init.h>  #include <linux/kernel.h>   
#include <linux/fs.h> extern int register_notifier_chain(struct notifier_block *nb);  
#define NOTIFIER_TCHAIN_NUMBER 520111 /* realize the notifier_call func */  
int receiver_notifier_chain_event(struct notifier_block *nb, unsigned long event,  void *v)  
{  switch(event){  case NOTIFIER_TCHAIN_NUMBER:  printk(KERN_DEBUG "^_^very great!received a notifier chain event!^_^\n");  break;  default:  break;  }  return NOTIFY_DONE;  
}  
/* define a notifier_block */  
static struct notifier_block receiver_notifier_chain = {  .notifier_call = receiver_notifier_chain_event,  
};  
static int __init receiver_notifier_chain_init(void)  
{  printk(KERN_DEBUG "receiver_notifier_chain_init\n");  register_notifier_chain(&receiver_notifier_chain); return 0;  
}  static void __exit receiver_notifier_chain_exit(void)  
{  printk(KERN_DEBUG "receiver_notifier_chain_exit!\n");  
}  MODULE_LICENSE("GPL");  
MODULE_AUTHOR("weifanghai");  module_init(receiver_notifier_chain_init);  
module_exit(receiver_notifier_chain_exit);  
对应的Makefile

obj-m +=receiver_notifier_chain.o KDIR := /home/weifanghai/Android_4.4_git/xunwei/kernel/iTop4412_Kernel_3.0
PWD ?= $(shell pwd)all:make -C $(KDIR) M=$(PWD) modulesclean:rm -rf *.o


3、send_notifier_chain.c(注: 发出通知链事件

#include <linux/notifier.h>  
#include <linux/module.h>  
#include <linux/init.h>  
#include <linux/kernel.h> 
#include <linux/fs.h> extern int invoke_notifiers_chain(unsigned long val, void *v);  
#define NOTIFIER_TCHAIN_NUMBER 520111 static int __init send_nofifier_chain_init(void)  
{  printk(KERN_DEBUG "send_nofifier_chain_init\n");  invoke_notifiers_chain(NOTIFIER_TCHAIN_NUMBER, "your are very beautiful!");  return 0;  
}  static void __exit send_nofifier_chain_exit(void)  
{  printk(KERN_DEBUG "send_nofifier_chain_exit\n");  
}  MODULE_LICENSE("GPL v2");  
MODULE_AUTHOR("weifanghai");  module_init(send_nofifier_chain_init);  
module_exit(send_nofifier_chain_exit);  

对应Makefile

obj-m +=send_notifier_chain.o KDIR := /home/weifanghai/Android_4.4_git/xunwei/kernel/iTop4412_Kernel_3.0
PWD ?= $(shell pwd)all:make -C $(KDIR) M=$(PWD) modulesclean:rm -rf *.o

运行三个ko,测试打印结果效果图:



小结:从打印信息看成功发送消息和接受消息,到这里关于Linux内核通知链notifier 的学习你应该有初步的认识了吧。