Linux内核通知链notifier

1.内核通知链表简介(引用网络资料)
    大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。
   
通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。其实和系统调用signal的思想差不多。

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

2.内核通知链表数据结构
    通知链表的节点类型为notifier_block,其定义如下:

struct notifier_block {

int (*notifier_call)(struct notifier_block *, unsigned long, void *);

struct notifier_block *next;

int priority;
};

notifier_call:该节点所对应的要运行的函数。
*next:指向下一个节点,事件发生时,依次执行的

3.内核通知链注册函数:
    在通知链注册时,需要有一个链表头,它指向这个通知链表的第一个元素。这样,之后的事件对该链表通知时就会根据这个链表头而找到这个链表中所有的元素。链表头的定义见本文第6节。
    注册的函数是:

/*

*
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_register 函数注册回调函数,该函数是按照优先级将回调函数加入到通知链中去的。

卸载的函数是:
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;
}

将节点n从nl所指向的链表中删除。
在kernel/notifier.c中内核根据通知链的类型分别包装了下面这几个函数:

int atomic_notifier_chain_register(struct atomic_notifier_head *nh,

struct notifier_block *n)

int blocking_notifier_chain_register(struct blocking_notifier_head *nh,

struct notifier_block *n)

int raw_notifier_chain_register(struct raw_notifier_head *nh,

struct notifier_block *n)

int srcu_notifier_chain_register(struct srcu_notifier_head *nh,

struct notifier_block *n)

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

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

在kernel/notifier.c中内核根据通知链的类型分别包装了上面这个函数:

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)

5.通知链四种类型

(5.1)原子通知链的链头
通知链元素的回调函数(当事件发生时要执行的函数)只能在中断上下文中运行,不允许阻塞。
struct atomic_notifier_head {

spinlock_t lock;

struct notifier_block *head;
};
(5.2)可阻塞通知链:
通知链元素的回调函数在进程上下文中运行,允许阻塞。
struct blocking_notifier_head {

struct rw_semaphore rwsem;

struct notifier_block *head;
};
(5.3)原始通知链:
对通知链元素的回调函数没有任何限制,所有锁和保护机制都由调用者维护。
struct raw_notifier_head {

struct notifier_block *head;
};
(5.4)SRCU 通知链:
可阻塞通知链的变种。
struct srcu_notifier_head {

struct mutex mutex;

struct srcu_struct srcu;

struct notifier_block *head;
};

6)定义一个通知链的头部结点并初始化:
include/linux/Notifier.h
初始化宏定义:

#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)

Linux内核调试方法总结之内核通知链的更多相关文章

  1. 【转】Linux内核调试方法总结

    目录[-] 一  调试前的准备 二  内核中的bug 三  内核调试配置选项 1  内核配置 2  调试原子操作 四  引发bug并打印信息 1  BUG()和BUG_ON() 2  dump_sta ...

  2. Linux内核调试方法总结

    Linux内核调试方法总结 一  调试前的准备 二  内核中的bug 三  内核调试配置选项 1  内核配置 2  调试原子操作 四  引发bug并打印信息 1  BUG()和BUG_ON() 2   ...

  3. Linux内核调试方法总结之反汇编

    Linux反汇编调试方法 Linux内核模块或者应用程序经常因为各种各样的原因而崩溃,一般情况下都会打印函数调用栈信息,那么,这种情况下,我们怎么去定位问题呢?本文档介绍了一种反汇编的方法辅助定位此类 ...

  4. Linux内核调试方法总结之序言

    本系列主要介绍Linux内核死机.异常重启类稳定性问题的调试方法. 在Linux系统中,一切皆为文件,而系统运行的载体,是一类特殊的文件,即进程.因此,我尝试从进程的角度分析Linux内核的死机.异常 ...

  5. Linux内核调试方法总结【转】

    转自:http://my.oschina.net/fgq611/blog/113249 内核开发比用户空间开发更难的一个因素就是内核调试艰难.内核错误往往会导致系统宕机,很难保留出错时的现场.调试内核 ...

  6. Linux内核调试方法【转】

    转自:http://www.cnblogs.com/shineshqw/articles/2359114.html kdb:只能在汇编代码级进行调试: 优点是不需要两台机器进行调试. gdb:在调试模 ...

  7. Linux内核调试方法总结之ptrace

    ptrace [用途] 进程跟踪器,类似于gdb watch的调试方法 [原理][详细说明参考man ptrace帮助文档] ptrace系统调用主要是父进程用来观察和控制子进程的执行过程.检查并替换 ...

  8. Linux内核调试方法总结之ddebug

    [用途] Linux内核动态调试特性,适用于驱动和内核各子系统调试.动态调试的主要功能就是允许你动态的打开或者关闭内核代码中的各种提示信息.适用于驱动和内核线程功能调试. [使用方法] 依赖于CONF ...

  9. Linux内核调试方法总结之调试宏

    本文介绍的内核调试宏属于静态调试方法,通过调试宏主动触发oops从而打印出函数调用栈信息. 1) BUG_ON 查看bug处堆栈内容,主动制造oops Linux中BUG_ON,WARN_ON用于调试 ...

随机推荐

  1. C++中的const分析

    1,C 语言中的 const: 1,const 修饰的变量是只读的,本质还是变量: 1,C 语言中的 const 使变量具有只读属性: 2,const 只在编译期有用,在运行期无用: 3,const ...

  2. oracle系统视图SQL语句整理

    -- DBA/ALL/USER/V_$/GV_$/SESSION/INDEX开头的绝大部分都是视图 -- DBA_TABLES意为DBA拥有的或可以访问的所有的关系表. -- ALL_TABLES意为 ...

  3. 【JZOJ 3909】Idiot 的乘幂

    题面: 正文: 把题目中的方程组组合在一起就变成了: \(X^{a+c}\equiv b \cdot d (\mod p)\) 那这时,我们假定两个数\(x\)和\(y\),使得: \(ax + cy ...

  4. win10上的Django项目实现内网映射

    网上有很多关于django项目发布uwsgi模块在 windows上的下载以及安装过程, 可是他们也并没有真正的在windows上使用到这个模块(至少目前在网上还没看到过), 而大部分人就是在Djan ...

  5. 小白学Python——用 百度翻译API 实现 翻译功能

    本人英语不好,很多词组不认识,只能借助工具:百度翻译和谷歌翻译都不错,近期自学Python,就想能否自己设计一个百度翻译软件呢? 百度翻译开放平台: http://api.fanyi.baidu.co ...

  6. Cypher 语句实战

    Cypher 语句实战 下载和安装 Neo4j windows 桌面版- 环境设置 https://www.w3cschool.cn/neo4j/neo4j_exe_environment_setup ...

  7. RabbitMQ几种队列模式

  8. 剑指offer-重构二叉树-树-python

    题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7, ...

  9. window下lamp环境搭建

    软件: apache_2.2.25.msi php-5.4.30-Win32-VC9-x86.zip mysql-5.6.11-win32.msi下载地址:http://download.csdn.n ...

  10. mac 命令行终端 设置代理

    环境: macOS Mojave 10.14.3 iTrem 2 3.2.8 酸酸乳1.1.4.4-R8 查看自己命令行的状态 curl ip.gs 正式开始 一.首先检查自己的酸酸乳是否正常,并在高 ...