文章目录
内核源码中相关文件
- /kernel/notifier.c
- /include/linux/notifier.h
1、通知链简介
文本基于内核源码
4.19.4
描述构成通知链的具体数据结构和API接口,同时描述四种通知链的具体应用场景,并对API接口进行简要分析。
在Linux内核中,struct notifier_block
是一种数据结构,用于实现观察者模式。它允许内核的不同部分将自己注册为监听器(观察者)以侦听特定事件。当这些事件发生时,内核会通知所有注册的notifier block,它们可以对事件做出适当的响应。
struct notifier_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;
};
-
notifier_call
:这个字段指向在通知事件发生时将被调用的回调函数。回调函数的函数签名定义为int (*notifier_call)(struct notifier_block *nb, unsigned long action, void *data)
。nb
参数是指向 notifier block 本身的指针,action
包含通知类型,而data
则是指向与事件相关的附加数据的指针。 -
next
:这个字段是指向链中下一个 notifier block 的指针。Linux内核维护一个已注册的 notifier block 的链表,该字段使得可以遍历整个链表。 -
priority
:这个字段决定了该 notifier block 相对于其他已注册 notifier block 的优先级。当多个块为同一事件注册时,内核按照优先级降序通知它们。具有较高优先级值的 notifier block 将在具有较低优先级值的之前收到通知。
要使用 struct notifier_block
,内核模块可以使用Linux内核提供的函数进行注册,例如register_inotifier()
或 register_netdevice_notifier()
,具体取决于特定的事件类别。
一些常见的利用 struct notifier_block
的事件包括:
- 文件系统事件,如文件创建、删除和修改。
- 网络设备事件,如接口的启用或禁用。
- 内存管理事件,如页面分配和释放。
通过使用 struct notifier_block
,内核开发人员可以更好地设计模块化和可扩展的系统,让不同的组件以解耦的方式对事件做出响应。这种模式有助于更好地组织代码,并且在不影响现有代码的情况下更容易添加新功能到内核中。
整个结构如下图所示:
2、通知链的类型
在linux内核中,定义了四种类型的通知链。
- (1)原子(Atomic)通知链
定义如下:
原子通知链在内核中广泛应用,特别是在一些基本的通知机制中。这种通知链的处理是原子的,意味着在处理链上的通知时,不会被中断或其他并发操作干扰。原子通知链的应用场景包括进程退出通知、进程停止通知、以及内核调试和跟踪事件通知等。
- (2)阻塞(Block)通知链
定义如下:
阻塞通知链用于一些需要等待通知链中所有处理器完成后才能继续执行的场景。当某个处理器在链上发起通知后,阻塞通知链将等待所有处理器都完成其任务后才返回。阻塞通知链的应用场景包括内核模块的初始化,其中一个模块可能需要等待其他模块完成初始化后才能继续执行。
- (3)原始(RAW)通知链
定义如下:
原始通知链是一种特殊类型的通知链,它没有任何同步机制。这意味着在处理通知链时,不进行任何锁定或同步操作,这可能会导致并发问题。原始通知链主要用于一些低层的底层通知机制,通常需要使用者自己确保线程安全性。原始通知链的应用场景相对较少,可能只在一些特定的高性能场景中使用。
- (4)SRCU通知链
定义如下:
SRCU通知链是通过Linux内核中的SRCU(Synchronize RCUs)机制来实现的。SRCU通知链提供了更高级的同步机制,以确保在删除或释放通知处理器时,不会出现竞态条件。这允许在通知链上安全地添加和删除处理器。SRCU通知链的应用场景包括网络设备事件通知,其中多个处理器可能对事件做出响应,并且需要在处理器安全删除时保持同步。
3、原理分析和API
(1)注销通知器
在使用通知链之前,需要创建对应类型的通知链,并使用注册进行注册,从源码角度,每种类型的通知链都一一对应着一个注册函数:
-
原子通知链注册函数:
int atomic_notifier_chain_register(struct atomic_notifier_head *nh,struct notifier_block *nb)
。 -
阻塞通知链注册函数:
int atomic_notifier_chain_register(struct blocking_notifier_head *nh,struct notifier_block *nb)
。 -
原始通知链注册函数:
int atomic_notifier_chain_register(struct raw_notifier_head *nh,struct notifier_block *nb)
。 -
srcu通知链注册函数:
int atomic_notifier_chain_register(struct srcu_notifier_head *nh,struct notifier_block *nb)
。
上述四种类型的注册函数本质上是调用notifier_chain_register()
函数实现核心功能,该函数实现如下:
static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n)
{
while ((*nl) != NULL) {
if (n->priority > (*nl)->pr