通知链描写叙述

大多数内核子系统都是相互独立的,因此某个子系统可能对其他子系统产生的事件感兴趣。

为了满足这个需求,也即是让某个子系统在发生某个事件时通知其他的子系统。Linux内核提供了通知链的机制。通知链表仅仅可以在内核的子系统之间使用,而不可以在内核与用户空间之间进行事件的通知。

    通知链表是一个函数链表,链表上的每个节点都注冊了一个函数。当某个事情发生时,链表上全部节点相应的函数就会被运行。所以对于通知链表来说有一个通知方与一个接收方。

在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注冊了某个函数,在发生某个事件时这些函数就得到运行。

通知链技术能够概括为:事件的被通知者将事件发生时应该运行的操作通过函数指针方式保存在链表(通知链)中,然后当事件发生时通知者依次运行链表中每个元素的回调函数完毕通知。

通知链实现

下面代码来自与linux-3.6.10

/kernel/notifier.c        通知链相关函数的实现

/include/linux/notifier.h 通知链结构、宏等定义

眼下代码实现由四种类型的通知链:

原子通知链(Atomic notifier chains):通知链元素回调函数执行在中断或者原子上下文。不同意堵塞

堵塞通知链(Blocking notifier chains):通知链元素回调函数执行在进程上下文,同意堵塞

原始通知链(Raw notifier chains):通知链元素回调函数执行上下文没有限制。全部锁和保护机制都由调用者维护。

SRCU通知链(SRCU notifier chains):一种堵塞通知链的变种,拥有和堵塞通知链相同的限制,与堵塞通知链的差别在于它使用可睡眠的RCU锁(Sleepable Read-Copy Update)而不是读写信号量来保护通知链。

也就意味着它运行回调函数(也即srcu_notifier_call_chain())的开销是非常小的,由于不须要缓存反弹和内存屏障。作为代价。srcu_notifier_chain_unregister()的开销是相当大的。

所以SRCU通知链适合用在常常通知notifier_blocks在通知链上非常少被去注冊的情况。

struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
struct notifier_block __rcu *next;
int priority;
}; struct atomic_notifier_head {
spinlock_t lock;
struct notifier_block __rcu *head;
}; struct blocking_notifier_head {
struct rw_semaphore rwsem;
struct notifier_block __rcu *head;
}; struct raw_notifier_head {
struct notifier_block __rcu *head;
}; struct srcu_notifier_head {
struct mutex mutex;
struct srcu_struct srcu;
struct notifier_block __rcu *head;
};

各种通知链表头的定义

/* srcu_notifier_heads must be initialized and cleaned up dynamically */
extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
#define srcu_cleanup_notifier_head(name) \
cleanup_srcu_struct(&(name)->srcu); #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)

notifier_chain_register()

加入一个元素到通知链,从notifier_chain_register()实现来看,通知链时按优先级从大到小的有序链表。

另外notifier_chain_cond_register()与此不同的是查找是否有同样的通知链元素,假设有则直接返回。

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

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

notifier_call_chain()

通知已注冊模块相应的事件

/**
* 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 __kprobes 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;
}

下面函数均是对上面三个函数的封装,仅仅只是同步保护机制不同

atomic_notifier_chain_register()

atomic_notifier_chain_unregister()

atomic_notifier_call_chain()

blocking_notifier_chain_register()

blocking_notifier_chain_cond_register()

blocking_notifier_chain_unregister()

blocking_notifier_call_chain()

raw_notifier_chain_register()

raw_notifier_chain_unregister()

raw_notifier_call_chain()

srcu_notifier_chain_register()

srcu_notifier_chain_unregister()

srcu_notifier_call_chain()

Linux内核通知链模块的更多相关文章

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

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

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

    转自:http://blog.chinaunix.net/uid-23069658-id-4364171.html 关于内核通知链不像Netlink那样,既可以用于内核与用户空间的通信,还能用于内核不 ...

  3. [Linux] 内核通知链 notifier

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

  4. Linux 内核通知链机制的原理及实现

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

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

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

  6. Linux内核调试方法总结之内核通知链

    Linux内核通知链notifier 1.内核通知链表简介(引用网络资料)    大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣.为了满足这个需求,也即是让某个子系统在 ...

  7. 从基本理解到深入探究 Linux kernel 通知链(notifier chain)【转】

    转自:https://blog.csdn.net/u014134180/article/details/86563754 版权声明:本文为博主原创文章,未经博主允许不得转载.——Wu_Being ht ...

  8. Linux内核通知链分析【转】

    转自:http://www.cnblogs.com/jason-lu/articles/2807758.html Linux内核通知链分析 1. 引言 Linux是单内核架构(monolithic k ...

  9. 解析 Linux 内核可装载模块的版本检查机制

    转自:http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmodules/ 为保持 Linux 内核的稳定与可持续发展,内核在发展过程中引进了可 ...

随机推荐

  1. Arduino可穿戴教程之第一个程序——连接硬件选择板子(二)

    Arduino可穿戴教程之第一个程序——连接硬件选择板子(二) 2.4.2  连接硬件 在选择好示例程序后就可以将LilyPad通过LilyPad编程器连接到电脑了. 2.4.3  选择板子 如果你了 ...

  2. open -python操作文件

    一打开文件 二操作文件 三关闭文件 open(文件,模式,编码),打开文件----->0101010(以二进制的方式打开)------>编码(open默认utf-8编码)------> ...

  3. RxSwift 系列(九)

    前言 看完本系列前面几篇之后,估计大家也还是有点懵逼,本系列前八篇也都是参考RxSwift官方文档和一些概念做的解读.上几篇文章概念性的东西有点多,一时也是很难全部记住,大家脑子里面知道有这么个概念就 ...

  4. Wordpress,你好!

    [caption id="" align="alignleft" width="1024"] 耳机[/caption] 想了想,还是没有删掉 ...

  5. 【BZOJ 2728】 2728: [HNOI2012]与非 (线性基?)

    2728: [HNOI2012]与非 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 813  Solved: 389 Description Inpu ...

  6. [BZOJ3140][HNOI2013]消毒(二分图最小点覆盖)

    3140: [Hnoi2013]消毒 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1621  Solved: 676[Submit][Status] ...

  7. bzoj 1030

    dp[i][j] 表示,在AC自动机中,从根节点开始,走了i条边,并且经过的点不包含危险节点,走到了j节点的路径数. 收获: 1.正难则反 2.一个字符串不包含给定pattern中的任何一个,则该字符 ...

  8. bzoj 3223 文艺平衡树 Splay 打标志

    是NOI2003Editor的一个子任务 #include <cstdio> #include <vector> #define maxn 100010 using names ...

  9. Velocity模板学习(一)

    一.Velocity是什么 Velocity是一个基于Java的模板引擎,允许任何人仅仅简单地使用模板语言就可以引用由Java代码编写的对象. 二.Velocity的基本语法 1.变量 变量的定义 在 ...

  10. Linux知识(6)----VIM

    vi的第一版是由Bill Joy在1978年写成的,当时他是UC Berkeley的学生.后来他共同创建了神奇的Sun公司.vi来源于visual一词,目标是在终端上可视化地模拟文本的编辑,是的更人性 ...