内核的很多子系统之间具有很强的相互依赖关系,其中一个子系统发现的或产生的事件,其他子系统可能都有兴趣。为了实现这种交互需求,Linux使用了通知链(Notification Chain)机制。

通知链这种机制只在内核的子系统之间使用,内核和用户空间之间的通知信息有其他更多的机制。在网络子系统中通知链机制使用频繁,如管理路由表的子系统会根据网络设备子系统的通知来更新路由表。

通知链其实就是一份简单的函数列表,当给定事件发生时被执行,每个函数让另一个子系统知道调用此函数的子系统内所发生的一个事件或者子系统侦测到的一个事件。每条通知链都有被动端(被通知者)和主动端(通知者),也就是所谓的发布-订阅模型(publish-and-subscribe): 被通知者(notified)就是接收某个事件的子系统,并提供回调函数;

通知者(notifier)就是侦测到某个事件后调用注册子系统的回调函数;

通知链允许每个子系统和其他子系统共享发生的事件,而无需知道就是是哪些子系统产生事件以及为何感兴趣。通知链中的每个列表元素的类型是:

struct notifier_block {

int (*notifier_call)(struct notifier_block *, unsigned long, void *); //回调函数

struct notifier_block __rcu *next; //列表中下一个元素

int priority; //优先级,优先级高的先执行,但一般都没有指定优先级,因此也就是根据注册先后顺序调用

};

notifier_block的实例常见名称有:xxx_chain、xxx_notifier_chain以及xxx_notifier_list。

当一个内核组件对给定通知链的事件感兴趣时,可以调用通用函数notifier_chain_register予以注册: 操作 函数原型

注册 通用函数:static int notifier_chain_register(struct notifier_block **nl,

struct notifier_block *n)

注册由读写锁同步保护的链:

int blocking_notifier_chain_register(struct blocking_notifier_head *nh,

struct notifier_block *n)

注册由自旋锁保护的链:

int atomic_notifier_chain_register(struct atomic_notifier_head *nh,

struct notifier_block *n)

注册没有保护的链,需要用户自己保证访问同步:

int raw_notifier_chain_register(struct raw_notifier_head *nh,

struct notifier_block *n)

注册inetaddr_chain通知链(读写锁保护):

int register_inetaddr_notifier(struct notifier_block *nb)

注册inet6addr_chain通知链(自旋锁保护):

int register_inet6addr_notifier(struct notifier_block *nb)

注册netdev_chain通知链(原始链):

int register_netdevice_notifier(struct notifier_block *nb)

注销 static int notifier_chain_unregister(struct notifier_block **nl,

struct notifier_block *n)

int blocking_notifier_chain_register(struct blocking_notifier_head *nh,

struct notifier_block *n)

int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,

struct notifier_block *n)

int raw_notifier_chain_unregister(struct raw_notifier_head *nh,

struct notifier_block *n)

int unregister_inetaddr_notifier(struct notifier_block *nb)

int unregister_inet6addr_notifier(struct notifier_block *nb)

int unregister_netdevice_notifier(struct notifier_block *nb)

调用 nl是通知链表头;val传递给回调函数的参数,表示发生事件类型;v传递给回调函数的参数指针;nr_to_call将要调用的元素个数;nr_calls实际调用的回调元素个数。最后一个回调函数返回返回值

static int __kprobes notifier_call_chain(struct notifier_block **nl,

unsigned long val, void *v, int nr_to_call,    int *nr_calls)

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)

不同保护机制的通知链的链表头结构:

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;

};

这三种通知链有专门的链表头,而通用函数操作的通知链的链表头就是一个元素。

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

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;

}

回调函数返回值:

#define NOTIFY_DONE        0x0000        /* Don't care */

#define NOTIFY_OK        0x0001        /* Suits me */

#define NOTIFY_STOP_MASK    0x8000        /* Don't call further */

#define NOTIFY_BAD        (NOTIFY_STOP_MASK|0x0002)

/* Bad/Veto action */

/*

* Clean way to return from the notifier and stop further calls.

*/

#define NOTIFY_STOP        (NOTIFY_OK|NOTIFY_STOP_MASK)

内核定义了很多种不同的通知链,网络子系统中常用的两个通知链主要是: inetaddr_chain:发送有关本地接口上的IPv4地址的插入、删除以及变更的通知信息。IPv6使用类似的inet6addr_chain;

netdev_chain:发送有关网络设备注册状态的通知信息。

某些网络接口设备驱动程序也许会注册reboot_notifer_list链,当系统重启时,会得到通知。

在net/ipv4/fib_frontend.c中注册通知链的实例:

static struct notifier_block fib_inetaddr_notifier = {

.notifier_call = fib_inetaddr_event,

};

static struct notifier_block fib_netdev_notifier = {

.notifier_call = fib_netdev_event,

};

void __init ip_fib_init(void)

{

…………………………

register_netdevice_notifier(&fib_netdev_notifier);

register_inetaddr_notifier(&fib_inetaddr_notifier);

…………………………

}

linux c 网络事件 通知,深入理解Linux网络技术内幕—通知链相关推荐

  1. 【Linux网络编程】深入理解Linux五种网络IO模型

    相信很多从事linux后台开发工作的都接触过同步&异步.阻塞&非阻塞这样的概念,也相信都曾经产生过误解,比如认为同步就是阻塞.异步就是非阻塞,下面我们先剖析下这几个概念分别是什么含义. ...

  2. linux内存管理与设计,深入理解Linux内存管理机制(一)

    通过本文,您即可以: 1. 存储器硬件结构: 2.分段以及对应的组织方式: 3.分页以及对应的组织方式. 注1:本文以Linux内核2.6.32.59本版为例,其对应的代码可以在http://www. ...

  3. linux内存利用率多少合适,如何理解linux服务器中的内存使用率和平均负载

    我使用的是具有128GB内存和24个内核的 Linux服务器.我使用top来查看它的使用量.它的输出粘贴在帖子的末尾.这是两个问题: (1)我看到每个正在运行的进程占用很小的内存百分比(%MEM不超过 ...

  4. linux应用对物理内存映射,深入理解Linux内存映射机制 (1)

    一. 绪 论 二. X86的硬件寻址方法 三. 内核对页表的设置 四. 实例分析映射机制 一. 绪 论 我们经常在程序的反汇编代码中看到一些类似0x32118965这样的地址,操作系统中称为线性地址, ...

  5. linux执行class文件_深入理解linux内核——可执行文件执行过程(2)

    接上篇.. 13.调用do_mmap()函数创建一个新线性区来对可执行文件正文段(即代码)进行映射.这个线性区的起始线性地址依赖于可执行文件的格式,因为程序的可执行代码通常是不可重定位的.因此,这个函 ...

  6. linux 文件可执行_深入理解linux内核——可执行文件执行过程(2)

    接上篇.. 13.调用do_mmap()函数创建一个新线性区来对可执行文件正文段(即代码)进行映射.这个线性区的起始线性地址依赖于可执行文件的格式,因为程序的可执行代码通常是不可重定位的.因此,这个函 ...

  7. 深入理解 Linux 内核

    Linux 内核系列文章 Linux 内核设计与实现 深入理解 Linux 内核 深入理解 Linux 内核(二) Linux 设备驱动程序 Linux设备驱动开发详解 文章目录 Linux 内核系列 ...

  8. 深入理解Linux虚拟内存管理(一)

    系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...

  9. 深入理解Linux虚拟内存管理(二)

    系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...

最新文章

  1. “文艺复兴” ConvNet卷土重来,压过Transformer FAIR重新设计纯卷积新架构
  2. java学习与总结:操作系统
  3. Adapter适配器和base-adapter-helper库的使用
  4. eve星战前夜登录提示服务器维护中,EVE星战前夜进不去怎么办 游戏进不去问题解决方法...
  5. Linux内核用户权限的实现,Linux内核设计与实现(6)---系统调用
  6. 三维建模软件:Rhino 7 for Mac
  7. android环境混合app开发,cordova混合App开发:Cordova+Vue实现Android (环境搭建)
  8. 程序设计基础C语言电子书,程序设计基础..pdf
  9. Eclipse的Servers视图中无法添加Tomcat6/Tomcat7
  10. 计算机视觉教程3-1:全面详解图像边缘检测算法(附Python实战)
  11. 考研二战日记——第二天 高数第一章第二节:数列的极限
  12. win10 Linux双系统教程,win10+ubuntu双系统超详细教程(亲测可用)
  13. 计算机学生会大型户外活动,学生会户外活动策划方案
  14. 微信公众平台一直限制配置失败-106
  15. 计算机组装考核记录表,计算机组装与维护考核方案(必修).doc
  16. linux查找与替换练习
  17. 设计心理学中的映射交互设计概念|优漫动游
  18. MYSQL命令集大全
  19. Remix本地环境搭建
  20. des算法s盒java实现_DES算法详解

热门文章

  1. VMware Pro 14.1.2 官方正式版及激活密钥
  2. Linux 定时任务 定时备份mysql数据
  3. Word文档处理控件TX Text Control .NET for WPF
  4. 地铁线路图的设计与实现
  5. 安装sql时挂起的解决方法
  6. java声明一个函数_java – 如何声明一个函数参数来接受抛出的函数?
  7. oracle sequences优化_性能优化-Oracle RAC中的Sequence Cache问题
  8. php注册树模式,PHP三种基本设计模式(工厂模式、单例模式、注册树模式)
  9. unity算法面试_Unity面试题汇总
  10. shell中用grep查找并且不输出_grep awk 搜索日志常用命令