Linux 核心維護一個全域網絡設備鏈表,所有網絡設備(物理網卡、虛擬設備、橋端口等)均通過 register_netdevice 註冊入鏈。本文深度剖析該函數的完整執行流程,揭示設備名稱分配、索引分配、哈希表插入、協議族通知等核心機制。
int register_netdev(struct net_device *dev)
{
rtnl_lock(); // RTNL 鎖保護全域設備鏈表
// 1. 名稱分配:支援格式化字串(如 "eth%d")
if (strchr(dev->name, '%')) {
err = dev_alloc_name(dev, dev->name);
if (err < 0) goto out;
}
if (dev->name[0] == '\0' || dev->name[0] == ' ') {
err = dev_alloc_name(dev, "eth%d"); // 默認前綴
if (err < 0) goto out;
}
// 2. 核心註冊邏輯
err = register_netdevice(dev);
out:
rtnl_unlock();
return err;
}
該函數(net/core/dev.c)執行以下關鍵步驟:
BUG_ON(dev_boot_phase);
ASSERT_RTNL(); // 必須持有 RTNL 鎖
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
spin_lock_init(&dev->queue_lock);
spin_lock_init(&dev->xmit_lock);
dev->xmit_lock_owner = -1;
// ... ingress_lock 等初始化
if (dev->init) {
ret = dev->init(dev); // 驅動特定初始化(如 PHY 啟動)
if (ret) { if (ret > 0) ret = -EIO; goto out_err; }
}
if (!dev_valid_name(dev->name)) { ret = -EINVAL; goto out_err; }
// 分配全域唯一 ifindex
dev->ifindex = dev_new_index();
if (dev->iflink == -1) dev->iflink = dev->ifindex;
// 名稱哈希表查重
head = dev_name_hash(dev->name);
hlist_for_each(p, head) {
struct net_device *d = hlist_entry(p, struct net_device, name_hlist);
if (!strncmp(d->name, dev->name, IFNAMSIZ)) {
ret = -EEXIST; goto out_err;
}
}
// SG+CSUM 組合合法性檢查
if ((dev->features & NETIF_F_SG) &&
!(dev->features & (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM))) {
dev->features &= ~NETIF_F_SG;
}
// 默認 rebuild_header
if (!dev->rebuild_header) dev->rebuild_header = default_rebuild_header;
// 狀態標記
set_bit(__LINK_STATE_PRESENT, &dev->state);
dev->next = NULL;
dev_init_scheduler(dev); // Qdisc 初始化
write_lock_bh(&dev_base_lock);
// 1. 插入全域設備鏈表尾部(dev_base 鏈表)
*dev_tail = dev;
dev_tail = &dev->next;
// 2. 插入名稱哈希表
hlist_add_head(&dev->name_hlist, head);
// 3. 插入索引哈希表
hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex));
write_unlock_bh(&dev_base_lock);
// 通知各協議族(IPv4、IPv6、Packet 等)新設備註冊
raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
// sysfs 節點創建(/sys/class/net/ethX)
ret = netdev_register_kobject(dev);
if (ret) goto out_err;
// 設置註冊狀態
dev->reg_state = NETREG_REGISTERED;
| 結構 | 類型 | 用途 |
|---|---|---|
dev_base | struct net_device * | 全域設備單向鏈表頭 |
dev_name_head[32] | struct hlist_head[32] | 名稱哈希表(Jenkins hash) |
dev_index_head[32] | struct hlist_head[32] | 索引哈希表 |
dev_base_lock | rwlock_t | 保護上述三個結構 |
netdev_chain | raw_notifier_head | 設備事件通知鏈 |
enum netdev_reg_state {
NETREG_UNINITIALIZED, // 初始狀態
NETREG_REGISTERED, // 已註冊,可見於全域
NETREG_UNREGISTERING, // 註銷中
NETREG_UNREGISTERED, // 已註銷
NETREG_RELEASED, // 內存已釋放
NETREG_DUMMY, // 佔位設備
};
register_netdevice 是網絡設備進入核心視野的「大門」,其設計體現了:
netdev_chain 廣播事件理解此流程,是掌握 Linux 網絡虛擬化、容器網絡、流量控制等高級特性的基礎。