文章目录
内核源码中相关文件
1、通知链简介
文本基于内核源码4.19.4描述构成通知链的具体数据结构和API插口,同时描述四种通知链的具体应用场景,并对API插口进行简略剖析。
在Linux内核中linux内核启动流程图,structnotifier_block是一种数据结构,用于实现观察者模式。它容许内核的不同部份将自己注册为窃听器(观察者)以侦听特定风波。当这种风波发生时,内核会通知所有注册的notifierblock,它们可以对风波作出适当的响应。
structnotifier_block在Linux内核头文件include/linux/notifier.h中定义,并具有以下结构:
struct notifier_block {
int (*notifier_call)(struct notifier_block *nb, unsigned long action, void *data);
struct notifier_block *next;
int priority;
};
要使用structnotifier_block,内核模块可以使用Linux内核提供的函数进行注册,比如register_inotifier()或register_netdevice_notifier(),具体取决于特定的风波类别。
一些常见的借助structnotifier_block的风波包括:
通过使用structnotifier_blocklinux命令行,内核开发人员可以更好地设计模块化和可扩充的系统linux内核启动流程图,让不同的组件以前馈的方法对风波作出响应。这些模式有助于更好地组织代码,但是在不影响现有代码的情况下更容易添加新功能到内核中。
整个结构如右图所示:
2、通知链的类型
在linux内核中,定义了四种类型的通知链。
定义如下:
原子通知链在内核中广泛应用,非常是在一些基本的通知机制中。这些通知链的处理是原子的,意味着在处理链上的通知时,不会被中断或其他并发操作干扰。原子通知链的应用场景包括进程退出通知、进程停止通知、以及内核调试和跟踪风波通知等。
定义如下:
阻塞通知链用于一些须要等待通知链中所有处理器完成后才会继续执行的场景。当某个处理器在链上发起通知后,阻塞通知链将等待所有处理器都完成其任务后才返回。阻塞通知链的应用场景包括内核模块的初始化,其中一个模块可能须要等待其他模块完成初始化后才会继续执行。
定义如下:
原始通知链是一种特殊类型的通知链,它没有任何同步机制。这意味着在处理通知链时,不进行任何锁定或同步操作,这可能会造成并发问题。原始通知链主要用于一些低层的底层通知机制,一般须要使用者自己确保线程安全性。原始通知链的应用场景相对较少,可能只在一些特定的高性能场景中使用。
定义如下:
SRCU通知链是通过Linux内核中的SRCU(SynchronizeRCUs)机制来实现的。SRCU通知链提供了更中级的同步机制,以确保在删掉或释放通知处理器时,不会出现竞态条件。这容许在通知链上安全地添加和删掉处理器。SRCU通知链的应用场景包括网路设备风波通知,其中多个处理器可能对风波作出响应,但是须要在处理器安全删掉时保持同步。
3、原理剖析和API(1)注销通知器
在使用通知链之前,须要创建对应类型的通知链,并使用注册进行注册,从源码角度,每种类型的通知链都一一对应着一个注册函数:
上述四种类型的注册函数本质上是调用notifier_chain_register()函数实现核心功能,该函数实现如下:
static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n)
{
while ((*nl) != NULL) {
if (n->priority > (*nl)->priority)
break;
nl = &((*nl)->next);
}
n->next = *nl;
rcu_assign_pointer(*nl, n);
return 0;
}
上述代码是一个按照优先级进行循环遍历的操作,假如n的优先级比*nl的优先级高这么循环结束,接着就将n插入到*nl的后面。产生通知链。
(2)注销通知器
有注册函数,则对应着注销函数,四种通知链的注销函数是:
上述四种类型的注册函数本质上是调用notifier_chain_unregister()函数实现核心功能,该函数实现如下:
static int notifier_chain_unregister(struct notifier_block **nl,
struct notifier_block *n)
{
while ((*nl) != NULL) {
if ((*nl) == n) {
rcu_assign_pointer(*nl, n->next);
return 0;
}
nl = &((*nl)->next);
}
return -ENOENT;
}
循环判定找到了要注销的之后执行注销,将其从数组中移除。
(3)通知链的通知
一般,通知链的注册是由各个模块在内核初始化阶段进行的。当特定风波发生时,内核会调用相应的notifier_call_chain()函数,以通知所有注册的模块或组件。这样,不同的模块可以按照风波类型和参数进行自定义处理,而无需显式地晓得其他模块的存在。
四种通知链分别对应不同的函数:
上述四个函数最后还会调用notifier_call_chain()实现核心功能linux查看端口占用,该函数实现如下:
static int notifier_call_chain(struct notifier_block **nl,
unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{
int ret = NOTIFY_DONE;
struct notifier_block *nb, *next_nb;
nb = rcu_dereference_raw(*nl);
while (nb && nr_to_call) {
next_nb = rcu_dereference_raw(nb->next);
#ifdef CONFIG_DEBUG_NOTIFIERS
if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
WARN(1, "Invalid notifier called!");
nb = next_nb;
continue;
}
#endif
ret = nb->notifier_call(nb, val, v);
if (nr_calls)
(*nr_calls)++;
if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
break;
nb = next_nb;
nr_to_call--;
}
return ret;
}
在notifier_chain_unregister()的while循环结构中会调用:
ret = nb->notifier_call(nb, val, v);
依次执行注册到该通知链中的所有函数。
4、实例代码
本小节通过原子通知链给出实例代码,原子通知链可用于实现观察者模式的通讯机制。
(1)定义一个通知链
#include
#include
#include
#include /* printk() */
//定义原子通知链
static ATOMIC_NOTIFIER_HEAD(my_notifier_list);
//通知事件
static int call_notifiers(unsigned long val, void *v)
{
return atomic_notifier_call_chain(&my_notifier_list, val, v);
}
EXPORT_SYMBOL(call_notifiers);
//向通知链注册通知block
static int register_notifier(struct notifier_block *nb)
{
int err;
err = atomic_notifier_chain_register(&my_notifier_list, nb);
if(err)
return err;
}
EXPORT_SYMBOL(register_notifier);
//从通知链中注销通知block
static int unregister_notifier(struct notifier_block *nb)
{
int err;
err = atomic_notifier_chain_unregister(&my_notifier_list, nb);
if(err)
return err;
}
EXPORT_SYMBOL(unregister_notifier);
static int __init myNotifier_init(void)
{
printk("myNotifier init finishn");
return 0;
}
static void __exit myNotifier_exit(void)
{
printk("myNotifier exit finishn");
}
module_init(myNotifier_init);
module_exit(myNotifier_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("iriczhao");
(2)实现观察者模块
/**
* 模块1,用于创建通知block,并注册
*/
#include
#include
#include
extern int register_notifier(struct notifier_block *nb);
extern int unregister_notifier(struct notifier_block *nb);
static int notifier_one_call_fn(struct notifier_block *nb,
unsigned long action, void *data)
{
printk(">>this is notifier_one_call_fnn");
printk("recv action = %d data = %pn",action,data);
return 0;
}
static int notifier_two_call_fn(struct notifier_block *nb,
unsigned long action, void *data)
{
printk(">>this is notifier_two_call_fnn");
return 0;
}
/* define a notifier_block */
static struct notifier_block notifier_one = {
.notifier_call = notifier_one_call_fn,
};
static struct notifier_block notifier_two = {
.notifier_call = notifier_two_call_fn,
};
static int __init module_1_init(void)
{
register_notifier(¬ifier_two);
register_notifier(¬ifier_one);
return 0;
}
static void __exit module_1_exit(void)
{
unregister_notifier(¬ifier_two);
unregister_notifier(¬ifier_one);
}
module_init(module_1_init);
module_exit(module_1_exit);
//定义模块相关信息
MODULE_AUTHOR("iriczhao");
MODULE_LICENSE("GPL");
(3)风波发生模块
/*
* 事件通知模块
*/
#include
#include
#include
#include
extern int call_notifiers(unsigned long val, void *v);
static int event_module_init(void)
{
printk("Event module initializedn");
unsigned long event = 123;
void *data = (void *)0xDEADBEEF;
call_notifiers(event, data);
return 0;
}
static void event_module_exit(void)
{
printk("Event module exitingn");
}
module_init(event_module_init);
module_exit(event_module_exit);
//定义模块相关信息
MODULE_AUTHOR("iriczhao");
MODULE_LICENSE("GPL");
(4)输出结果
将上述三份代码以模块形式建立,并加载进内核,首先加载自定义的通知链my_notifier_list,接着加载module_1.ko注册两个风波订阅者,最后加载module_2.ko通知风波,并向module_1发送两个参数:action和data,并通过module_1复印下来。输出结果如下: