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. 搜索专题: HDU1026Ignatius and the Princess I

    Ignatius and the Princess I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  2. http-proxy-middleware

    概述 这是设置代理的神器,webpack的devServer.proxy就是使用了非常强大的 http-proxy-middleware 包.Node.js代理很简单. 轻松配置代理中间件进行连接,发 ...

  3. golang 一个字符串表达式替换的函数

    package util import ( "fmt" "reflect" "regexp" "strconv" &qu ...

  4. vue设置全局样式变量 less

    1.第一步: npm install sass-resources-loader --save-dev 2.然后在build 的utils.js中exports.cssLoaders = functi ...

  5. iOS微信浏览器回退不刷新(监听浏览器回退事件)

    兼容性:兼容全部ios系统 $(function(){ pushHistory(); }); function pushHistory(){ window.addEventListener(" ...

  6. IDEA 不自动复制资源文件到编译目录 classes 的问题

    复制文件后建议编译项目

  7. nologin - 阻止非root用户登录系统

    描述 DESCRIPTION 如果存在文件 /etc/nologin, login(1) 将只允许root访问.其它用户的登录会遭到拒绝并且显示该文件中的内容给他们. 文件 FILES /etc/no ...

  8. 基于Kintex Ultrasacle的万兆网络光纤 PCIe加速卡416 光纤PCIe卡

    基于Kintex Ultrasacle的万兆网络光纤 PCIe加速卡 一.产品概述 本卡为企业级别板卡,可用于数据中心,安全领域数据采集处理.标准PCI Express全高板,适用于普通服务器.工作站 ...

  9. DMA方式的数据传送过程

      DMA方式具有如下特点: 1. 外部设备的输入输出请求直接发给主储存器. 主存储器既可以被CPU访问,也可以被外围设备访问.因此,在主存储器中通常要有一个存储管理部件来为各种访问主存储器的申请排队 ...

  10. 035-OpenStack 关闭安全组

    OpenStack Neutron的安全组默认会对每个网口开启MAC/IP过滤功能(防arp欺骗),不是该网口的MAC/IP发出的包会被宿主机丢弃.这种限制会导致vNF的上行网口转发的数据包被丢弃,无 ...