引入

在linux内核中,各个子系统之间有很强的相互关系,某些子系统可能对其他子系统产生的事件比较感兴趣。因此内核引入了notifier机制,当然了notifier机制只能用在内核子系统之间,不能用在内核与应用层之间。比如当系统suspend的时候,就会使用到notifier机制来通知系统的内核线程进行suspend。
内核实现的notifier机制代码位于kernel/kernel/notifier.c,同时此机制的代码量也不是很多只有600行左右。

数据结构

内核使用struct notifier_block结构代表一个notifier
typedef int (*notifier_fn_t)(struct notifier_block *nb,unsigned long action, void *data);struct notifier_block {notifier_fn_t notifier_call;struct notifier_block __rcu *next;int priority;
};

notifier_call:  代表当事件发生之后调用的回调函数。

next:             用来链接同一个类型的notifier。
priority:         notifier chain的优先级。对应的数字越大优先级越高,就优先执行。
同时内核也提供了四种不同类型的notifier chain
  • 原子通知链(Atomic notifier chains)
struct atomic_notifier_head {spinlock_t lock;struct notifier_block __rcu *head;
};

可以看到原子notifier chain只是对notifier_block的一个封装。同时atomic notifier chain的回调函数需要运行在中断上下文/原子上下文中,而且不能睡眠。

很明显因为atomic_notifer_head其中的spin_lock的特点就是不能睡眠。
  • 可阻塞通知链(Blocking notifier chains)
struct blocking_notifier_head {struct rw_semaphore rwsem;struct notifier_block __rcu *head;
};

blocking_notifier_head其中包含了读写信号量成员rwsem,而信号量的特定就是运行在进程上下文,而且还可以睡眠。同理Blocking notifier chains的回调函数特征一样。

  • 原始通知链(Raw notifier chains)
struct raw_notifier_head {struct notifier_block __rcu *head;
};

raw_notifier_head的特点是对回调函数,register, unregister都没有任何限制,所有的保护机制都需要调用者维护。

  • SRCU通知链(SRCU notifier chains)
struct srcu_notifier_head {struct mutex mutex;struct srcu_struct srcu;struct notifier_block __rcu *head;
};

SRCU通知链是block notifier chain的一种变体,采用SRCU(Sleepable Read-Copy Update)代替rw-semphore来保护chains

notifier chain初始化

内核提供了一套宏用来初始化各个类型的通知链
#define ATOMIC_INIT_NOTIFIER_HEAD(name) do {   \spin_lock_init(&(name)->lock);  \(name)->head = NULL;       \} while (0)
#define BLOCKING_INIT_NOTIFIER_HEAD(name) do {  \init_rwsem(&(name)->rwsem); \(name)->head = NULL;       \} while (0)
#define RAW_INIT_NOTIFIER_HEAD(name) do {   \(name)->head = NULL;       \} while (0)

以上是动态初始化各个类型的通知链,当然了有动态初始化,也就有静态初始化

#define ATOMIC_NOTIFIER_INIT(name) {               \.lock = __SPIN_LOCK_UNLOCKED(name.lock),  \.head = NULL }
#define BLOCKING_NOTIFIER_INIT(name) {              \.rwsem = __RWSEM_INITIALIZER((name).rwsem),   \.head = NULL }
#define RAW_NOTIFIER_INIT(name) {               \.head = NULL }
/* srcu_notifier_heads cannot be initialized statically */#define ATOMIC_NOTIFIER_HEAD(name)                \struct atomic_notifier_head name =            \ATOMIC_NOTIFIER_INIT(name)
#define BLOCKING_NOTIFIER_HEAD(name)                \struct blocking_notifier_head name =          \BLOCKING_NOTIFIER_INIT(name)
#define RAW_NOTIFIER_HEAD(name)                 \struct raw_notifier_head name =               \RAW_NOTIFIER_INIT(name)

通过注释可以知道SRCU通知链不能使用静态的方法,因此内核提供了一个动态的初始化函数,

void srcu_init_notifier_head(struct srcu_notifier_head *nh)
{mutex_init(&nh->mutex);if (init_srcu_struct(&nh->srcu) < 0)BUG();nh->head = NULL;
}

注册/注销通知链

内核提供的最基本的注册通知链的函数
/**   Notifier chain core routines.  The exported routines below* are layered on top of these, with appropriate locking added.*/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;
}

上述的操作就是通过判断priority的大小,然后将大的插入带链表头,小的插入在链表末尾。

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;
}

上述的注销函数,就是先找到此节点,然后从链表中删除的一个操作。

因为插入/删除操作都是临界资源,需要使用rcu机制保护起来。
同样,内核通过包装核心的注册/注销函数,实现了上述说的四种notifier chain
int atomic_notifier_chain_register(struct atomic_notifier_head *nh,struct notifier_block *n)
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,struct notifier_block *n)int blocking_notifier_chain_register(struct blocking_notifier_head *nh,struct notifier_block *n)
int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,struct notifier_block *n)int raw_notifier_chain_register(struct raw_notifier_head *nh,struct notifier_block *n)
int raw_notifier_chain_unregister(struct raw_notifier_head *nh,struct notifier_block *n)int srcu_notifier_chain_register(struct srcu_notifier_head *nh,struct notifier_block *n).
int srcu_notifier_chain_unregister(struct srcu_notifier_head *nh,struct notifier_block *n)

通知函数

当某种事件需要发生的时候,就需要调用内核提供的通知函数notifier call函数,来通知注册过相应时间的子系统。
/*** notifier_call_chain - Informs the registered notifiers about an event.*  @nl:       Pointer to head of the blocking notifier chain* @val:      Value passed unmodified to notifier function*   @v:        Pointer passed unmodified to notifier function* @nr_to_call:   Number of notifier functions to be called. Don't care*         value of this parameter is -1.* @nr_calls: Records the number of notifications sent. Don't care*          value of this field is NULL.*   @returns:  notifier_call_chain returns the value returned by the*          last notifier function called.*/
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);ret = nb->notifier_call(nb, val, v);          //调用注册的回调函数if (nr_calls)(*nr_calls)++;if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)  //有停止的mask就返回,否则继续break;nb = next_nb;nr_to_call--;}return ret;
}

同样内核也提供了四个不同类型的通知函数

int atomic_notifier_call_chain(struct atomic_notifier_head *nh,unsigned long val, void *v)
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,unsigned long val, void *v)
int raw_notifier_call_chain(struct raw_notifier_head *nh,unsigned long val, void *v)
int srcu_notifier_call_chain(struct srcu_notifier_head *nh,unsigned long val, void *v)

示例分析

通过编写两个文件,一个用来注册事件,另一个用来通知事件。
notifier.c用来注册事件
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>BLOCKING_NOTIFIER_HEAD(test_chain_head);
EXPORT_SYMBOL_GPL(test_chain_head);int register_test_notifier(struct notifier_block *nb)
{return blocking_notifier_chain_register(&test_chain_head, nb);
}int unregister_test_notifier(struct  notifier_block *nb)
{return blocking_notifier_chain_unregister(&test_chain_head, nb);
}static int test_chain_notify(struct notifier_block *nb,unsigned long mode, void *_unused)
{printk(KERN_EMERG "notifier: test_chain_notify!\n");        //回调处理函数return 0;
}static struct notifier_block test_chain_nb = {.notifier_call = test_chain_notify,
};static int notifier_test_init(void)
{printk(KERN_EMERG "notifier: notifier_test_init!\n");        register_test_notifier(&test_chain_nb);                       //注册notifier事件return 0;
}static void notifier_test_exit(void)
{printk(KERN_EMERG "notifier: notifier_test_exit!\n");unregister_test_notifier(&test_chain_nb);
}module_init(notifier_test_init);
module_exit(notifier_test_exit);
MODULE_LICENSE("GPL v2");

call.c用来触发事件。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>extern struct blocking_notifier_head test_chain_head;static int call_notifier_call_chain(unsigned long val)
{int ret = blocking_notifier_call_chain(&test_chain_head, val, NULL);return notifier_to_errno(ret);
}static int call_test_init(void)
{printk(KERN_EMERG "notifier: call_test_init!\n");call_notifier_call_chain(123); //在init函数中触发事件return 0;
}static void call_test_exit(void)
{printk(KERN_EMERG "notifier: call_test_exit!\n");
}
module_init(call_test_init);
module_exit(call_test_exit);
MODULE_LICENSE("GPL v2");

测试结构如下:

root@test:/data # insmod notifier.ko
root@test:/data # insmod call.ko
root@test:/data # dmesg | grep "notifier"
[   89.644596] c7 notifier: notifier_test_init!
[   95.956801] c6 notifier: call_test_init!
[   95.960624] c6 notifier: test_chain_notify!

Linux内核通知链(Notifier)相关推荐

  1. 深入理解Linux内核通知链(Notifier)

    数据结构 内核使用struct notifier_block结构代表一个notifier typedef int (*notifier_fn_t)(struct notifier_block *nb, ...

  2. linux 内核通知,[Linux] 内核通知链 notifier

    Linux 内核中每个模块之间都是独立的,如果模块需要感知其他模块的事件,就需要用到内核通知链. 最典型的通知链应用就是 LCD 和 TP 之间,TP 需要根据 LCD 的亮灭来控制是否打开关闭触摸功 ...

  3. linux 通知链,Linux内核通知链notifier

    当有事件发生时,通知者调用 notifier_call_chain 函数通知事件的到达,这个函数会遍历n1指向的通知链中所有的元素,然后依次调用每一个的回调函数,完成通知动作. static int ...

  4. Linux内核通知链(notifier chain)

    1.概述 Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制( ...

  5. Linux 内核通知链和例程代码

    概念 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制.通知链表只 ...

  6. Linux 内核通知链随笔【中】

        关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不同子系统之间的通信,通知链只能用于内核不同子系统之间的通信.那么内核通知链到底是怎么工作的?我们如何才能用好 ...

  7. Linux内核通知链机制的原理及实现【转】

    转自:http://www.cnblogs.com/armlinux/archive/2011/11/11/2396781.html 一.概念: 大多数内核子系统都是相互独立的,因此某个子系统可能对其 ...

  8. notifier_chain 内核通知链的学习与使用

    linux 内核通知链 通知链用于将状态改变信息发送给请求这些改变的代码段,通知链可以用于内核将特定的事件传递给感兴趣的内核组件中. 内核定义了主要的事件类型: 死亡通知 网络设备通知 cpu 频率通 ...

  9. Linux内核基础--事件通知链(notifier chain)【转】

    转自:http://blog.csdn.net/wuhzossibility/article/details/8079025 内核通知链 1.1. 概述 Linux内核中各个子系统相互依赖,当其中某个 ...

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

    概念 1.Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制( ...

最新文章

  1. LeetCode简单题之子数组最大平均数 I
  2. UVA10537 The Toll! Revisited (思维、最短路、输出字典序最小路径)
  3. 聚类热图怎么按自己的意愿调整分支的顺序?
  4. [转]Entity Framework走马观花之把握全局
  5. 希尔排序的基本原理及实现
  6. 部署时服务端Excel的COM设置
  7. 同志亦凡人第五季/全集BQueer As Folk 5迅雷下载
  8. ClickHouse内核分析-MergeTree的存储结构和查询加速
  9. 【华为云技术分享】如何拆分用户故事
  10. ABP之展现层(Datatables分页)
  11. python连接mysql用哪个模块_pymysql模块使用---Python连接MySQL数据库
  12. 动态修改css 规则
  13. [nsis]安装包界面乱码问题
  14. naivcat 破解安装教程(永久)
  15. router走差分_route
  16. IDEA 插件开发实战
  17. 中文人物关系图谱构建与应用项目(人物关系抽取,关系抽取评测)
  18. 服务器显示504,帮您解决win7系统访问nginx服务器提示504 Gateway Time-out错误的修复技巧...
  19. EXFAT文件系统DBR的完美恢复
  20. 【春节档排片地域可视化分析】

热门文章

  1. Servlet请求转发RequestDispatcher接口
  2. mysql(一主从从)
  3. 玩转Metasploit系列(第二集)
  4. 用VS调试 javascript
  5. list遍历_Python遍历list,使用range和enumerate的效率区别
  6. echarts环形图加边框
  7. opencv提供的带参数例程
  8. C#通过XElement写入XML文件
  9. POJ1061 青蛙的约会(拓展欧几里德)
  10. [Android] 混音线程MixerThread