Linux網絡協議棧的內核初始化工作大致分為設備鏈路層(e100_module_init、net_dev_init)、網絡層(inet_init)、傳輸層(proto_init)、應用層(sock_init)初始化。由於物理層為具體的網絡設備,所以內核對網絡協議棧的實現以及網卡驅動實現等沒有物理層(L1),驅動以及設備的初始化函數都應該化為L2層。前面介紹了e100系列網卡驅動的實現,下面我們看看內核初始化的時候對設備初始化的另一個函數net_dev_init。
/*設備處理層的初始化函數*/
static int __init net_dev_init(void)
{
int i, rc = -ENOMEM;
/*沒有被初始化*/
BUG_ON(!dev_boot_phase);
/*該函數在/proc目錄下創建三個文件,主要用於讀取網絡相關統計數據
正如我們看到的,/proc下的文件基本都為只讀的,這裡提供的三個文件
都沒有寫操作*/
if (dev_proc_init())
goto out;
/*在/sysfs設備文件系統的class中注冊net節點*/
if (netdev_sysfs_init())
goto out;
/*初始化網絡處理函數鏈表和散列表,這些函數是用來處理接收到的不同
協議族報文*/
INIT_LIST_HEAD(&ptype_all);
for (i = 0; i < 16; i++)
INIT_LIST_HEAD(&ptype_base[i]);
/*下面為初始化存放網絡設備的散列表*/
/*散列表關鍵字由設備名稱計算獲得*/
for (i = 0; i < ARRAY_SIZE(dev_name_head); i++)
INIT_HLIST_HEAD(&dev_name_head[i]);
/*散列表關鍵字由設備接口索引計算獲得*/
for (i = 0; i < ARRAY_SIZE(dev_index_head); i++)
INIT_HLIST_HEAD(&dev_index_head[i]);
/*
* Initialise the packet receive queues.
*/
/*初始化與CPU相關的數據接收隊列*/
for_each_possible_cpu(i) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue);
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
set_bit(__LINK_STATE_START, &queue->backlog_dev.state);
queue->backlog_dev.weight = weight_p;
queue->backlog_dev.poll = process_backlog;
atomic_set(&queue->backlog_dev.refcnt, 1);
}
/*注冊網絡DMA客戶端*/
netdev_dma_register();
/*標志已經初始化*/
dev_boot_phase = 0;
/*注冊兩個軟件中斷用於數據接收和發送*/
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
/*在通知鏈表中注冊一個回調函數,用於相應CPU熱插拔事件
由回調函數可以看出,一旦接到通知,CPU輸入隊列中的包逐一
交由netif_rx()處理*/
hotcpu_notifier(dev_cpu_callback, 0);
/*初始化目的路由緩存,通知鏈的方式*/
dst_init();
/*初始化網絡鏈路層的組播模塊,在/proc/net下創建文件dev_mcast
用來存放內核中網絡設備與IP組播相關的參數*/
dev_mcast_init();
rc = 0;
out:
return rc;
}
Net_dev_init函數在驅動程序之前調用,用於初始化一些必要的信息,包括兩種設備隊列、數據輸入輸出隊列以及注冊兩個用於接收和發送數據的軟中斷等。另外,上面各種初始化函數的層次劃分可能不正確,只是便於理解。