深入理解linux网络技术内幕

1 系统初始化

1.1 通知链

为什么需要通知链?书中给出了一个例子,如果网络中一个子网不可达,必须更新路由器的路由表,而更新这个路由表就需要通知链。然后给出结论:In any case, the routing subsystem that manages the tables must be informed of the relevant information by some other subsystem, demonstrating the need for notification chains。在任何情况下,管理表的路由子系统都必须由其他子系统通知相关信息,这表明需要通知链。

通知链简单说就是给定事件发生时要执行的函数列表。通知链只用于内核子系统之间。内核和用户空间之间的通知采用其他机制。

其定义如下,也就是一串notifier_block组成的链表。

struct notifier_block
{
  int (*notifier_call)(struct notifier_block *self, unsigned long, void *); //要执行的函数
  struct notifier_block *next; //链中的下一个节点
  int priority; //优先级
};

notifier_block实例的常见名称为xxx_chain、xxx_notifier_chain和xxx_notiifier_list

1.1.1 订阅通知

当一个kernel模块对一个指定的通知链感兴趣,那么它可以使用通用的notifier_chain_register函数来进行订阅(注册)它。

int notifier_chain_register(struct notifier_block **list, struct notifier_block *n)
{
    write_lock(&notifier_lock);
    while(*list)
    {
        if(n->priority > (*list)->priority)
            break;
        list= &((*list)->next);
    }
    n->next = *list;
    *list=n;
    write_unlock(&notifier_lock);
    return 0;
}

除了这个通用的注册函数,还有一些包装函数可供我们使用,如下列出了订阅和取消订阅相关的包装函数

Registration:
  int notifier_chain_register(struct notifier_block **list, struct notifier_block *n)
  Wrappers:
    inetaddr_chain register_inetaddr_notifier //其中register_inetaddr_notifier是注册函数,执行该注册函数后将向inetaddr_chain这个通知链添加一个元素
    inet6addr_chain register_inet6addr_notifier
    netdev_chain register_netdevice_notifier
Unregistration:
  int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n)
  Wrappers:
    inetaddr_chain unregister_inetaddr_notifier
    inet6addr_chain unregister_inet6addr_notifier
    netdev_chain unregister_netdevice_notifier
Notification
  int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v)

1.1.2 通知链上的事件

通知是通过函数notifier_call_chain生成的。此函数会按优先级顺序调用所有链上注册的notifier_block节点中的回调例程(notifier_call)。

比如某个内核子模块某个task中调用了notifier_call_chain函数,导致通知链上的回调例程被调用,继而唤醒其他正在监听该回调例程的Task。

int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v) // val为事件类型
{
  int ret = NOTIFY_DONE;
  struct notifier_block *nb = *n;
  while (nb)
  {
    ret = nb->notifier_call(nb, val, v);
    if (ret & NOTIFY_STOP_MASK)
    {
      return ret;
    }
    nb = nb->next;
  }
  return ret;
}

回调函数会返回NOTIFY_OK,NOTIFY_DONE,NOTIFY_BAD,NOTIFY_STOP等值。注意看函数中,当出现NOTIFY_BAD,NOTIFY_STOP时将跳出循环并return ret;也就是说当通知链上某个事件回调函数执行异常时,将中止执行链上其他事件回调函数。

还有就是对于同一通知链,不同CPU同一时间执行notifier_call_chain函数,这时回调函数就要做好并发控制了。

1.1.3 网络子系统所用的通知链

内核定义了至少10种通知链,我们这里只关心网络中的通知链

inetaddr_chain:在本地接口上发送有关插入、删除和更改IP地址的通知。

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

 

posted @ 2024-05-16 21:34  zhenjingcool  阅读(40)  评论(0编辑  收藏  举报