Linux网络技术学习(三)—— 通知链


使用通知链的原因

图中是带有4个接口的Linux路由器。显示该路由器与5个网络之间的联系。
在这里插入图片描述
网络A直接连接至RT的接口eth0,而网络F没有直接连接到RT,但是RT的eth3直接连接至另一个路由器,其接口的地址为IP1,并且第二路由器知道怎么联系网络F。
总之,有些网络是直接连接的,而其他的网络则需要一个或多个附加的路由器协助才能联系到。

假设接口eth3由于网络短线使得管理命令(如ifconfig eth3 down)失效或者造成硬件失败,结果RT无法连接网络D/E/F,因此该从路由表中删除。谁将通知路由子系统该接口的失败? ———— 通知链
在这里插入图片描述
RT通过网络A和网络E而联系网络F。最初会选E是因为其成本较少,但现在E已经无法联系,因此,路由表应该更新网络F的路由该走网络A。这个过程涉及到一些本地主机事件,如设备注册和删除,以及路由器配置中的复杂因素和所用的路由协议等。

在任何情况下,管理路由表的路由子系统必须从其他子系统收到相关的信息的通知,这就有了通知链的需求。


什么是通知链?

通知链就是一份简单的函数列表,当给定的事件发生的时候就会运行。
每个函数都让另一个系统知道,调用此函数的子系统内发生的一件事情或者子系统能监测到的一件事情。
每条通知链都分为:被动端(被通知者)和主动端(通知者)
被通知者:就是要求接收某事件的子系统,而且会提供回调函数予以调用
通知者:就是感受到一件事情并调用回调函数的子系统
所执行的函数是由被通知的子系统所选取,绝不是链条的拥有者(产生通知信息的子系统)决定该执行什么函数。
拥有者只是定义这份列表而已;任何内核子系统都可以对该链表注册一个回调函数以接收通知信息。
在这里插入图片描述
假如一个通用函数如果不使用通知链,如何把一个事件通知给外部子系统?
在这里插入图片描述
必须为每个可能对一个时间感兴趣的子系统都引入一个条件子句。结果每次某个人添加一个子系统给内核,此子系统的维护者就要添加一个子句。

子系统维护者不可能去追踪每个被添加到内核中的子系统。然而,每个子系统维护者应该知道:
1、自己对来自其他子系统的哪种事件感兴趣。
2、自己知道的事件有哪几种,并且其他子系统可能感兴趣的事件又有哪些
通知链允许每个子系统和其他子系统共享发生的事件,而不去关心哪些子系统产生事件


定义链

通知链列表元素的类型是notifier_block

// 平台RK3568
// include/linux/notifier.h
struct notifier_block {
    notifier_fn_t notifier_call;                 // 要执行的函数
    struct notifier_block __rcu *next;           // 链接列表的元素
    int priority;                                // 该函数的优先值
};

链注册

当一个内核组件对给定通知链的事件感兴趣时,可以通过函数notifier_chain_register注册。内核提供一组包裹该函数的函数
在这里插入图片描述
在这里插入图片描述
对每条链条而言,那些notifier_block实体被插入到一个优先级排序的列表中。相同优先级的元素则按插入时间排序:新的元素排末尾
对通知链的访问受到notifier_lock锁保护。所有通知链只用一个锁不影响性能。因为子系统通常只是在引导或模块加载时注册其notifier_calla函数,后面都是对列表的访问是只读的(共享)
因为调用notifier_chain_register函数就是把回调函数插入到所有列表中,因此把列表指定为输入参数。


链上的通知事件

通知信息由notifier_call_chain产生,此函数只是按优先级次序调用对此链注册的所有回调函数。

// 平台:RK3568
// kernel/notifier.c
static int 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)
            break;
        nb = next_nb;
        nr_to_call--;
    }
    return ret;
}

nb 通知链
val 事件类型。链本身标识的一组事件
v 此输入参数可由用户所注册的处理函数使用

notifier_call_chain所有调用的回调函数可以返回值定义在<include/linux/notifier.h>

#define NOTIFY_DONE     0x0000                                // 对通知信息不感兴趣
#define NOTIFY_OK       0x0001                                // 通知信息被正确处理了
#define NOTIFY_STOP_MASK    0x8000                            // 由notifier_call_chain自己检查,是否停止调用回调函数
#define NOTIFY_BAD      (NOTIFY_STOP_MASK|0x0002)             // 有些事件出错了,停止调用此事件的回调函数
#define NOTIFY_STOP     (NOTIFY_OK|NOTIFY_STOP_MASK)          // 函数被正确调用,然而,这事件不需要再调用其他的回调函数

注:在同一时间上,不同的CPU上相同的通知链可能同时调用notifier_call_chain。回调函数的责任是在必要的地方处理互斥。


网络子系统的通知链

内核定义了至少10种不同 的通知链。和网口相关的主要有:

inetaddr_chain

发送有关本地接口上的IPv4地址的插入、删除以及变更的通知信息。

netdev_chain

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


参考文献

《Understanding Linux Network Internals》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bazinga bingo

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值