内核通知链(网络子系统为例)

概念

1.Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施。为满足这样的需求,内核实现了事件通知链机制(notification chain)。
2.通知链只能用在各个子系统之间,而不能在内核和用户空间进行事件的通知。组成内核的核心系统代码均位于kernel目录下,通知链表位于kernel/notifier.c中,对应的头文件为include/linux/notifier.h。
3.事件通知链表是一个事件处理函数的列表每个通知链都与某个或某些事件有关,当特定的事件发生时,就调用相应的事件通知链中的回调函数,进行相应的处理。(类似于设计模式中的观察者模式,解耦)

内核通知链类型

内核使用struct notifier_block结构代表一个notifier(include/linux/netifier.h)

struct notifier_block;
//struct notifier_block *nb:执行时,将包含该回调的struct notifiler结构传递进来;
//unsigned long action/void *data:由特定通知的定义者决定传递给回调的一些信息;
typedef	int (*notifier_fn_t)(struct notifier_block *nb,
			unsigned long action, void *data); //notifier回调函数类型定义
struct notifier_block {
//回调函数,也就是在特定事物处理时对其他模块的通知,具体通知到其他模块做什么,就是在这个回调函数中处理,因此这是由其他模块指定的(需要注册)
	notifier_fn_t notifier_call;   //回调函数
	//将需要的通知串联起来,在需要时逐个通知
	struct notifier_block __rcu *next;  //用于挂在通知链上
	int priority; //回调函数的优先级,一般默认为0,通知的优先级,根据优先级决定通知的先后,对应的数字越大优先级越高,就越优先执行
};

内核提供了四种不同的类型notifiler chain

原子通知链

之所以被称为原子通知链,是因为通知链元素的回调函数(当事件发生时要执行的函数)在中断或原子操作上下文中运行,不允许阻塞。

struct atomic_notifier_head {
	spinlock_t lock; //自动锁保护通知链
	struct notifier_block __rcu *head; //通知链元素的链
};

示意图(notifier block和 notifiler chain的数据结构组织方式)
在这里插入图片描述
原子通知链对应的API

include/linux/notifiler.h
1.初始化一个原子通知链
#define ATOMIC_NOTIFIER_HEAD(name)				\
	struct atomic_notifier_head name =			\
		ATOMIC_NOTIFIER_INIT(name)
2.注册一个notifiler block到通知链
extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
		struct notifier_block *nb);
3.发送一个事件到通知链上的notifiler block
extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
		struct notifier_block *nb);
4. 从通知链删除一个notifiler block
extern int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
		unsigned long val, void *v);

可阻塞通知链

通知链元素的回调函数在进程上下文中运行,允许阻塞。

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

可阻塞通知链对应的API

1.初始化一个阻塞通知链
#define BLOCKING_NOTIFIER_HEAD(name)				\
	struct blocking_notifier_head name =			\
		BLOCKING_NOTIFIER_INIT(name)
2.注册一个notifiler block通知链
extern int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
		struct notifier_block *nb);
3.从通知链删除一个notifier block
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
		struct notifier_block *nb);
4.发送一个事件到通知链上的notifier block
extern int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
		unsigned long val, void *v);

原始通知链

对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护。

struct raw_notifier_head {
	struct notifier_block __rcu *head;
};

原始通知链对应API

1.初始化一个原始通知链
#define RAW_NOTIFIER_HEAD(name)					\
	struct raw_notifier_head name =				\
		RAW_NOTIFIER_INIT(name)
2.注册一个notifiler block到通知链
extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
		struct notifier_block *nb);
3.从通知链删除一个notifiler blcok
extern int raw_notifier_chain_unregister(struct raw_notifier_head *nh,
		struct notifier_block *nb);
4.发送一个事件到通知链上的notifiler block
extern int raw_notifier_call_chain(struct raw_notifier_head *nh,
		unsigned long val, void *v);

SRCU通知链

可阻塞通知链的一种变体。

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

SRCU通知链对应的API

1.初始化一个SRCU通知链
#ifdef CONFIG_TREE_SRCU
#define _SRCU_NOTIFIER_HEAD(name, mod)				\
	static DEFINE_PER_CPU(struct srcu_data, name##_head_srcu_data); \
	mod struct srcu_notifier_head name =			\
			SRCU_NOTIFIER_INIT(name, name##_head_srcu_data)
#else
#define _SRCU_NOTIFIER_HEAD(name, mod)				\
	mod struct srcu_notifier_head name =			\
			SRCU_NOTIFIER_INIT(name, name)

#endif
2.注册一个notifiler block到通知链
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
		struct notifier_block *nb);
3.从通知链删除一个notifiler block
extern int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,
		struct notifier_block *nb);
4.发送一个事件到通知链上的notifiler block
extern int srcu_notifier_call_chain(struct srcu_notifier_head *nh,
		unsigned long val, void *v)

通知链实例1

#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/delay.h>
static RAW_NOTIFIER_HEAD(test_chain_head);   //定义通知链
#define EVENT_A 0x01    //定义通知链的事件类型
#define EVENT_B 0x02
//定义回调函数
int test_notifier_event(struct notifier_block *nb, unsigned long event, void *v)
{
    switch (event) {
    case EVENT_A:
        printk("test_notifier_event EVENT_A\n");
        break;
    case EVENT_B:
        printk("test_notifier_event EVENT_B\n");
        break;
    default:
        break;
    }
    return NOTIFY_DONE;
}
//定义notifier block
static struct notifier_block test_notifier = {
    .notifier_call = test_notifier_event,  //指定回调函数
};
static int __init mynotify_init(void)
{
    printk("raw_notifier_chain_register\n");
    raw_notifier_chain_register(&test_chain_head, &test_notifier); //注册chain,回调函数
    printk("raw_notifier_send_EVENT_B\n");
    raw_notifier_call_chain(&test_chain_head, EVENT_B, NULL); //发生消息
    printk("raw_notifier_send_EVENT_A\n");
    raw_notifier_call_chain(&test_chain_head, EVENT_A, NULL); //发送消息
    return 0;
}
static void __exit mynotify_exit(void)
{
    raw_notifier_chain_unregister(&test_chain_head, &test_notifier);
    printk("raw_notifier_chain_unregister\n");
}
module_init(mynotify_init);
module_exit(mynotify_exit);
MODULE_AUTHOR("chengxu zhou");
MODULE_LICENSE("GPL");
obj-m += notifiler_chain.o                         #由此目标文件生成模块
CURRENT_PATH := $(shell pwd)             #当前所在的路径
LINUX_KERNEL := $(shell uname -r)        #内核的版本号
 
#内核代码所在的路径
#LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
LINUX_KERNEL_PATH :=/lib/modules/$(shell uname -r)/build/
#编译
all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
 
#清除编译生成的文件
clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
sudo insmod notifiler_chain.ko
[438025.761353] raw_notifier_chain_register
[438025.761356] raw_notifier_send_EVENT_B
[438025.761358] test_notifier_event EVENT_B
[438025.761359] raw_notifier_send_EVENT_A
[438025.761361] test_notifier_event EVENT_A
sudo rmmod notifiler_chain

通知链实例2

示意图
在这里插入图片描述

//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(&notfier_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(&notfier_chain_fp, nb);  
    if(err)  
    {
       printk(KERN_DEBUG "raw_notifier_chain_register error!\n"); 
       goto out;  
    }
    else
       printk(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("chengxu zhou");  
module_init(notifier_chain_init);  
module_exit(notifier_chain_exit);
//receiver_notifier_chain.c(定义回调函数,定义notifiler_blcok,向netfier_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("chengxu zhou");
module_init(receiver_notifier_chain_init);
module_exit(receiver_notifier_chain_exit);
//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("chenxu zhou");  
module_init(send_nofifier_chain_init);  
module_exit(send_nofifier_chain_exit);  
obj-m += notifier_chain_register.o   receiver_notifier_chain.o  send_notifier_chain.o                    #由此目标文件生成模块
CURRENT_PATH := $(shell pwd)             #当前所在的路径
LINUX_KERNEL := $(shell uname -r)        #内核的版本号
 
#内核代码所在的路径
#LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
LINUX_KERNEL_PATH :=/lib/modules/$(shell uname -r)/build/
#编译
all:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
 
#清除编译生成的文件
clean:
	make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
sudo insmod notifier_chain_register.ko
sudo insmod receiver_notifier_chain.ko 
sudo insmod send_notifier_chain.ko
[439847.525992] notifier_chain_init
[439861.520401] receiver_notifier_chain_init
[439861.520405] raw_notifier_chain_register success!
[439873.444402] send_nofifier_chain_init
[439873.444409] invoke_notifiers_chain
[439873.444412] ^_^very great!received a notifier chain event!^_^

通知链实例3

//test_chain_0.c:声明一个通知链;向内核注册通知链;定义事件;导出函数符号
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>	/* printk() */
#include <linux/fs.h>	/* everything() */
#define TESTCHAIN_INIT 0x52U
static RAW_NOTIFIER_HEAD(test_chain); //初始化通知链
/* define our own notifier_call_chain */
static int call_test_notifiers(unsigned long val, void *v)
{
	return raw_notifier_call_chain(&test_chain, val, v);
}
EXPORT_SYMBOL(call_test_notifiers);
/* define our own notifier_chain_register func */
 static int register_test_notifier(struct notifier_block *nb)
{
	int err;
	err = raw_notifier_chain_register(&test_chain, nb);
	if(err)
		goto out;
out:
	return err;
}
EXPORT_SYMBOL(register_test_notifier);
static int __init test_chain_0_init(void)
{
	printk(KERN_DEBUG "I'm in test_chain_0\n");
	return 0;
}
static void __exit test_chain_0_exit(void)
{
	printk(KERN_DEBUG "Goodbye to test_chain_0\n");
//	call_test_notifiers(TESTCHAIN_EXIT, (int *)NULL);
}
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("chenxu zhou");
module_init(test_chain_0_init);
module_exit(test_chain_0_exit);
//test_chain_1.c:定义回调函数;定义notifier_block;向chain_0注册notifier_block
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>	/* printk() */
#include <linux/fs.h>	/* everything() */
extern int register_test_notifier(struct notifier_block *nb);
#define TESTCHAIN_INIT 0x52U
/* realize the notifier_call func */
int test_init_event(struct notifier_block *nb, unsigned long event,
	void *v)
{
	switch(event){
	case TESTCHAIN_INIT:
		printk(KERN_DEBUG "I got the chain event: test_chain_2 is on the way of init\n");
		break;
	default:
		break;
	}
	return NOTIFY_DONE;
}
/* define a notifier_block */
static struct notifier_block test_init_notifier = {
	.notifier_call = test_init_event,
};
static int __init test_chain_1_init(void)
{
	printk(KERN_DEBUG "I'm in test_chain_1\n");
	register_test_notifier(&test_init_notifier);	// 由chain_0提供的设施
	return 0;
}
static void __exit test_chain_1_exit(void)
{
	printk(KERN_DEBUG "Goodbye to test_clain_l\n");
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("chenxu zhou");
module_init(test_chain_1_init);
module_exit(test_chain_1_exit);
//test_chain_2.c:发出通知链事件
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>	/* printk() */
#include <linux/fs.h>	/* everything() */
extern int call_test_notifiers(unsigned long val, void *v);
#define TESTCHAIN_INIT 0x52U
static int __init test_chain_2_init(void)
{
	printk(KERN_DEBUG "I'm in test_chain_2\n");
	call_test_notifiers(TESTCHAIN_INIT, "no_use");
	return 0;
}
static void __exit test_chain_2_exit(void)
{
	printk(KERN_DEBUG "Goodbye to test_chain_2\n");
}
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("fishOnFly");
module_init(test_chain_2_init);
module_exit(test_chain_2_exit);

Makefile:

# Comment/uncomment the following line to disable/enable debugging
# DEBUG = y
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif
ifneq ($(KERNELRELEASE),)
# call from kernel build system
obj-m	:= test_chain_0.o test_chain_1.o test_chain_2.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
depend .depend dep:
	$(CC) $(CFLAGS) -M *.c > .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif

网络子系统的内核通知链

示意图
在这里插入图片描述
流程图(举一个例子)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值