概念
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(¬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;
}
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
网络子系统的内核通知链
示意图
流程图(举一个例子)