为什么要有内核通知表链:

    Linux由多个相互依赖的子系统组成。其中一些子系统可能需要对其他子系统的一些事件感兴趣。这样子系统之间需要一些通信机制来实现这一功能。
    在接触Notification Chain之前,我们可能想到通过轮询来实现,事件发生时,子系统轮询所有其他的子系统,看看有没有对这一事件感兴趣的,有没有需要执行的子函数。
If (subsystem_X_enabled) {
do_something_1
}
if (subsystem_Y_enabled) {
do_something_2
}
If (subsystem_Z_enabled) {
do_something_3
}
... ... ...

    这种方式看起来可以满足我们的需要,但是代码太繁杂了,会导致每个子系统变得很庞大。而Notification
Chain则大大的改善了这些不足。
Notification Chain基本原理
    Notification Chain是一种“发布——订阅”模型,当某个子系统发生某一事件时,它就通知所有对这一事件感兴趣的子系统,以便执行响应函数。在这个模型中,发生事件的子系统是主动端,对事件感兴趣的子系统是被动端。(本文也将被动端成为被通知端)。 在事件发生时,检测或产生事件的子系统作为主动一方通过通知函数来告知作为被动一方的订阅者(对此事件感兴趣的子系统)。这里有个额外要求,订阅一方要提供callback函数以供发布方调用,当然,提供什么样的callback函数完全由订阅方决定。

通知表链模型图:


Notification Chain源代码解析

   结构体:
//通知块
struct notifier_block
{
int (*notifier_call)(struct notifier_block *self, unsigned long, void *);//回调函数
struct notifier_block *next; //
int priority; //注册的优先级,用户自行指定,优先级越高回调函数就越早执行
};

通知表链的定义
Linux内核中有三种通知表链:
//原子通知表链
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;
};

需要定义一个通知表链时,用XXX_NOTIFIER_HEAD(name)来定义,下面以RAW_NOTIFIER_HEAD为例:
static RAW_NOTIFIER_HEAD(netdev_chain);

追踪 RAW_NOTIFIER_HEAD,
#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)

继续
#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 */

由此完成了一条通知表链的定义和初始化。

    Linux 内核中有许多个notification chain,我们常用到的有三个netdev_chain,inetaddr_chain,inet6addr_chain。结合struct notifier_block,每条chain上有一个回调函数的队列(链表)notifier_call ,那么这些回调函数(notification通知块)如何使用,合适调用呢。
    首先我们在穿件通知表链之初,通知表链只有链表头,没有其他的notifier_block块,这个链表会关注一些事件(如netdev_chain会关注设备加入删除等,具体下文会做更清楚的介绍。)如果某个子系统对这条chain关注的时间感兴趣,它就注册一个notifier_block到链表中。notifier_block块的注册和注销,以下面形式实现:
int notifier_chain_register(struct notifier_block **list, struct notifier_block *n)
int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n)

这两个是封装函数,具体的通知链都会对这两个函数进行封装,定义自己的注册和注销函数。以netdev_chain为例,封装过程:
int register_netdevice_notifier(struct notifier_block *nb)
{
raw_notifier_chain_register(&netdev_chain, nb);
}
int raw_notifier_chain_register(struct raw_notifier_head *nh,
struct notifier_block *n)
{
return notifier_chain_register(&nh->head, n);
}

某个子系统需要关注设备的变化信息时,就将处理函数notifier_call(自己编写的)作为notifier_block注册的通知表链。

    事件发生时,通知表链执行过程
    当事件发生时,时间会调用notifier_call_chain遍历通知表链中每个通知块里的回调函数,由回调函数确定是否要执行处理。
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;
}


    
    






























深入理解Linux网络技术内幕——Notification内核通知表链的更多相关文章

  1. 深入理解linux网络技术内幕读书笔记(三)--用户空间与内核的接口

    Table of Contents 1 概论 1.1 procfs (/proc 文件系统) 1.1.1 编程接口 1.2 sysctl (/proc/sys目录) 1.2.1 编程接口 1.3 sy ...

  2. 深入理解linux网络技术内幕读书笔记(四)--通知链

    Table of Contents 1 概述 2 定义链 3 链注册 4 链上的通知事件 5 网络子系统的通知链 5.1 包裹函数 5.2 范例 6 测试实例 概述 [注意] 通知链只在内核子系统之间 ...

  3. 深入理解Linux网络技术内幕——设备的注册与初始化(二)

    设备注册于设备除名     设备注册与设备除名一般有 register_netdev和unregister_netdev完成.这两个是包裹函数,负责上锁,真正起作用的是其调用的register_net ...

  4. 深入理解Linux网络技术内幕——用户空间与内核空间交互

    概述:     内核空间与用户空间经常需要进行交互.举个例子:当用户空间使用一些配置命令如ifconfig或route时,内核处理程序就要响应这些处理请求.     用户空间与内核有多种交互方式,最常 ...

  5. 深入理解linux网络技术内幕读书笔记(七)--组件初始化的内核基础架构

    Table of Contents 1 引导期间的内核选项 2 注册关键字 3 模块初始化代码 引导期间的内核选项 linux运行用户把内核配置选项传给引导记录,然后引导记录再把选项传给内核. 在引导 ...

  6. 深入理解Linux网络技术内幕——内核基础架构和组件初始化

    引导期间的内核选项     Linux允许用户把内核配置选项传给引导记录,再有引导记录传给内核,以便对内核进行调整.     start_kernel中调用两次parse_args,用于引导期间配置用 ...

  7. 《深入理解Linux网络技术内幕》阅读笔记 --- 路由基本概念

    一.路由的基本概念 1.一条路由就是一组参数,这些参数存储了往一个给定目的地转发流量所需的信息,而一条路由所需的最少的参数集合为:(1)目的网络,(2)出口设备,(3)下一跳网关 2.路由中的相关术语 ...

  8. 深入理解linux网络技术内幕读书笔记(十)--帧的接收

    Table of Contents 1 概述 1.1 帧接收的中断处理 2 设备的开启与关闭 3 队列 4 通知内核帧已接收:NAPI和netif_rx 4.1 NAPI简介 4.1.1 NAPI优点 ...

  9. 深入理解linux网络技术内幕读书笔记(九)--中断与网络驱动程序

    Table of Contents 1 接收到帧时通知驱动程序 1.1 轮询 1.2 中断 2 中断处理程序 3 抢占功能 4 下半部函数 4.1 内核2.4版本以后的下半部函数: 引入软IRQ 5 ...

随机推荐

  1. booststrap select2的应用总结

    本身对前端js了解不是特别多,在项目中,遇到很多前端的问题,有时间整理一下,有不对的地方,不吝赐教,多多批评指正. 在项目中,遇到最多的select下拉框情景,莫过于多选和单选了 单选是很容易理解的, ...

  2. Codeforces Round #289 (Div. 2, ACM ICPC Rules) E. Pretty Song 算贡献+前缀和

    E. Pretty Song time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  3. Jedis 之 初始<一>

    package xx.jedis; import java.util.Set; import redis.clients.jedis.Jedis; import redis.clients.jedis ...

  4. threejs和particles.js

    particles.js 在线配置的东西: http://vincentgarreau.com/particles.js/ https://threejs.org/ http://thehuub.co ...

  5. C#一套简单的单例系统

    单例基类 public class CSingletonBase<TYPE> { public static TYPE Singleton { get { return m_singlet ...

  6. 笔试题目练习-python

    以下内容包含笔试练习库的题目和代码,题目来自牛客网,仅供参考. # coding = utf-8 import sys def test1(): """ 题目描述:计算字 ...

  7. php file_get_contents计时读取一个文件/页面 防止读取不到内容

    php file_get_contents计时读取一个文件/页面 防止读取不到内容 $url = 'http://www.baidu.com/index.php'; $opts = array( 'h ...

  8. English trip -- VC(情景课)10 B Around the house 在家里

    Vocablulary focus  核心词汇 cook play the guitar listen to music watch TV read magazines work in the gar ...

  9. English trip -- MC(情景课)3 D

    xu言: have a nice weekend... sentences How many people are there in you family? they are 3 people in ...

  10. JDK1.5 新特性

    1:自动装箱与拆箱 自动装箱:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中. 自动拆箱:每当需要一个值时,被装箱对象中的值就被自动地提取出来,没必要再去调用intValue ...