Linux内核通知链(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: 代表当事件发生之后调用的回调函数。
- 原子通知链(Atomic notifier chains)
struct atomic_notifier_head {spinlock_t lock;struct notifier_block __rcu *head;
};
可以看到原子notifier chain只是对notifier_block的一个封装。同时atomic notifier chain的回调函数需要运行在中断上下文/原子上下文中,而且不能睡眠。
- 可阻塞通知链(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;
}
上述的注销函数,就是先找到此节点,然后从链表中删除的一个操作。
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_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)
示例分析
#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)相关推荐
- 深入理解Linux内核通知链(Notifier)
数据结构 内核使用struct notifier_block结构代表一个notifier typedef int (*notifier_fn_t)(struct notifier_block *nb, ...
- linux 内核通知,[Linux] 内核通知链 notifier
Linux 内核中每个模块之间都是独立的,如果模块需要感知其他模块的事件,就需要用到内核通知链. 最典型的通知链应用就是 LCD 和 TP 之间,TP 需要根据 LCD 的亮灭来控制是否打开关闭触摸功 ...
- linux 通知链,Linux内核通知链notifier
当有事件发生时,通知者调用 notifier_call_chain 函数通知事件的到达,这个函数会遍历n1指向的通知链中所有的元素,然后依次调用每一个的回调函数,完成通知动作. static int ...
- Linux内核通知链(notifier chain)
1.概述 Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制( ...
- Linux 内核通知链和例程代码
概念 大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制.通知链表只 ...
- Linux 内核通知链随笔【中】
关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不同子系统之间的通信,通知链只能用于内核不同子系统之间的通信.那么内核通知链到底是怎么工作的?我们如何才能用好 ...
- Linux内核通知链机制的原理及实现【转】
转自:http://www.cnblogs.com/armlinux/archive/2011/11/11/2396781.html 一.概念: 大多数内核子系统都是相互独立的,因此某个子系统可能对其 ...
- notifier_chain 内核通知链的学习与使用
linux 内核通知链 通知链用于将状态改变信息发送给请求这些改变的代码段,通知链可以用于内核将特定的事件传递给感兴趣的内核组件中. 内核定义了主要的事件类型: 死亡通知 网络设备通知 cpu 频率通 ...
- Linux内核基础--事件通知链(notifier chain)【转】
转自:http://blog.csdn.net/wuhzossibility/article/details/8079025 内核通知链 1.1. 概述 Linux内核中各个子系统相互依赖,当其中某个 ...
- 内核通知链(网络子系统为例)
概念 1.Linux内核中各个子系统相互依赖,当其中某个子系统状态发生改变时,就必须使用一定的机制告知使用其服务的其他子系统,以便其他子系统采取相应的措施.为满足这样的需求,内核实现了事件通知链机制( ...
最新文章
- LeetCode简单题之子数组最大平均数 I
- UVA10537 The Toll! Revisited (思维、最短路、输出字典序最小路径)
- 聚类热图怎么按自己的意愿调整分支的顺序?
- [转]Entity Framework走马观花之把握全局
- 希尔排序的基本原理及实现
- 部署时服务端Excel的COM设置
- 同志亦凡人第五季/全集BQueer As Folk 5迅雷下载
- ClickHouse内核分析-MergeTree的存储结构和查询加速
- 【华为云技术分享】如何拆分用户故事
- ABP之展现层(Datatables分页)
- python连接mysql用哪个模块_pymysql模块使用---Python连接MySQL数据库
- 动态修改css 规则
- [nsis]安装包界面乱码问题
- naivcat 破解安装教程(永久)
- router走差分_route
- IDEA 插件开发实战
- 中文人物关系图谱构建与应用项目(人物关系抽取,关系抽取评测)
- 服务器显示504,帮您解决win7系统访问nginx服务器提示504 Gateway Time-out错误的修复技巧...
- EXFAT文件系统DBR的完美恢复
- 【春节档排片地域可视化分析】