首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Linux 內核網絡子系統通知鏈機制深度解析

     編輯:Java 綜合教學

Linux 內核網絡子系統通知鏈機制深度解析

Linux 內核廣泛使用通知鏈實現模塊間松耦合的事件通知。網絡子系統通過 netdev_chain、inetaddr_chain 等通知鏈,讓路由、鄰居、防火牆等模塊對網卡狀態變更、IP 地址異動做出反應。本文從數據結構、註冊/註銷、調用流程、實戰案例四個維度深度剖析。

一、核心數據結構

struct notifier_block {
    int (*notifier_call)(struct notifier_block *nb, unsigned long val, void *v);
    struct notifier_block *next;
    int priority;  // 優先級,高者先執行,默認 0 按註冊順序
};
  • notifier_call:回調函數,參數 val 為事件類型(如 NETDEV_REGISTER),v 為事件數據(如 struct net_device*)
  • 返回值:NOTIFY_DONE(0) 不關心、NOTIFY_OK 處理成功、NOTIFY_BAD 出錯、NOTIFY_STOP 停止傳播

二、註冊與註銷

// 註冊:按優先級降序插入鏈表頭部
static int notifier_chain_register(struct notifier_block **nl, struct notifier_block *n) {
    while (*nl) {
        if (n->priority > (*nl)->priority) break;
        nl = &(*nl)->next;
    }
    n->next = *nl;
    rcu_assign_pointer(*nl, n);
    return 0;
}

// 註銷:遍歷鏈表移除
static int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n) {
    while (*nl) {
        if (*nl == n) {
            rcu_assign_pointer(*nl, n->next);
            return 0;
        }
        nl = &(*nl)->next;
    }
    return -ENOENT;
}

使用 RCU(Read-Copy-Update)保證無鎖讀取並發安全。

三、通知鏈調用流程

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(*nl);
    while (nb && nr_to_call) {
        next_nb = rcu_dereference(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;
}
  • 調用上下文:調用者的上下文(可能是進程上下文或中斷上下文)
  • nr_to_call=-1 表示遍歷整條鏈

四、網絡子系統核心通知鏈

通知鏈定義位置典型事件典型監聽者
netdev_chainnet/core/dev.cNETDEV_REGISTER/UNREGISTER/UP/DOWN/CHANGE/CHANGEMTU路由 (fib)、鄰居 (arp/ndisc)、橋接、防火牆、bonding/team
inetaddr_chainnet/ipv4/devinet.cNETDEV_UP/DOWN (IPv4 地址增刪)IGMP、路由、策略路由

五、實戰案例:路由子系統監聽 netdev_chain

1. 註冊入口

// net/ipv4/fib_frontend.c
void __init ip_fib_init(void) {
    register_netdevice_notifier(&fib_netdev_notifier);
}

static struct notifier_block fib_netdev_notifier = {
    .notifier_call = fib_netdev_event,
};

2. 封裝註冊函數:register_netdevice_notifier

int register_netdevice_notifier(struct notifier_block *nb) {
    rtnl_lock();
    int err = raw_notifier_chain_register(&netdev_chain, nb);
    if (err) goto unlock;

    // 關鍵:對「已存在」的網卡重放事件,讓新註冊者同步當前狀態
    if (!dev_boot_phase) {
        for_each_netdev(net, dev) {
            nb->notifier_call(nb, NETDEV_REGISTER, dev);
            if (dev->flags & IFF_UP)
                nb->notifier_call(nb, NETDEV_UP, dev);
        }
    }
unlock:
    rtnl_unlock();
    return err;
}

3. 事件處理回調:fib_netdev_event

static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) {
    struct net_device *dev = ptr;
    struct in_device *in_dev = __in_dev_get_rtnl(dev);

    if (event == NETDEV_UNREGISTER) {
        fib_disable_ip(dev, 2);  // 刪除該設備所有路由
        return NOTIFY_DONE;
    }
    if (!in_dev) return NOTIFY_DONE;

    switch (event) {
    case NETDEV_UP:
        for_ifa(in_dev) fib_add_ifaddr(ifa);  // 添加接口路由
        #ifdef CONFIG_IP_ROUTE_MULTIPATH
        fib_sync_up(dev);
        #endif
        rt_cache_flush(-1);
        break;
    case NETDEV_DOWN:
        fib_disable_ip(dev, 0);  // 刪除接口路由
        break;
    case NETDEV_CHANGEMTU:
    case NETDEV_CHANGE:
        rt_cache_flush(0);       // 刷新路由緩存
        break;
    }
    return NOTIFY_DONE;
}

六、關鍵設計亮點

  1. 異步解耦:事件產生者(網卡驅動)無需知道消費者(路由/鄰居)詳情
  2. 狀態同步:新註冊者自動重放歷史事件,保證視圖一致性
  3. 優先級控制:核心模塊(如路由)設置高優先級優先處理
  4. RCU 保護:無鎖遍歷,極低開銷,適合高頻網絡事件
  5. 停止傳播:NOTIFY_STOP 允許關鍵模塊攔截事件(如防火牆丟包)

七、開發者指南:如何添加自定義通知鏈監聽

  1. 定義 struct notifier_block nb = { .notifier_call = my_handler };
  2. 模塊初始化調用 register_netdevice_notifier(&nb) 或 register_inetaddr_notifier(&nb)
  3. 模塊退出調用對應 unregister_*_notifier
  4. 回調中不可睡眠(可能在中斷上下文),耗時操作放入 workqueue
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved