標籤:深入理解linux網路技術內幕 協議棧 網路裝置 裝置
概述 核心的初始化過程過程中,與網路相關的工作如下所示:
核心引導時執行start_kernel,start_kernel結束之前會調用rest_init,rest_init初始化核心線程init(在Linux3-12中為kernel_init)。
asmlinkage void __init start_kernel(void){ ... parse_early_param();//間接調用parse_args parse_args(...); //處理核心引導程式(boot loader)在引導期間傳給核心的參數, ... init_IRQ(); //初始化硬體中斷 tick_init(); init_timers(); //定時器用於支援後續初始化工作的運行。 hrtimers_init(); softirq_init(); //初始化軟體中斷 ... rest_init(); //這裡會通過kernel_thread函數調用核心線程init(kernel_init).}
static init __ref kernel_init(void *unused){ kernel_init_freeable(); //Linux-3.12 在這裡調用do_basic_setup free_initmem(); //用於釋放已經不再需要的記憶體。 ..... run_init_process(execute_command) ...}
static void __init do_basic_setup(void){ cpuset_init_smp(); usermodehelper_init(); shmem_init(); driver_init(); init_irq_proc(); do_ctors(); usermodehelper_enable(); do_initcalls(); //初始化核心子系統和內建的裝置驅動 random_int_secret_init();}
裝置的註冊和初始化 一個裝置要能夠正常工作,他就必須被核心所識別,並且與正確的驅動關聯起來。裝置驅動程式以私人結構體的形式儲存了驅動本裝置所需要的所有資訊,並且與其他和本裝置有互動的組件相互影響。
裝置的註冊和初始化一部分由核心完成,一部分由裝置驅動程式完成。
裝置的初始化包括了硬體初始化、軟體初始化、功能初始化三部分:
硬體初始化:由裝置驅動程式和通用匯流排層完成,有時也需要使用者提供一些參數。主要任務是將硬體功能配置成IRQ和I/O地址,以便能夠跟核心相互作用。軟體初始化:裝置使用之前必須關注當前配置或啟用的網路通訊協定, 一般需要使用者提供諸如ip地址之類的參數。功能初始化: Linux核心提供了一系列的網路選項,有些網路選項對每一個裝置都需要進行單獨配置(如實現Qos的子系統),這些配置決定了資料包進入隊列和離開裝置的出口隊列的方式 。 NIC初始化目標 網路裝置在Linux中都是以net_device執行個體進行初始化的,本節先不討論這個,本節主要介紹裝置驅動程式如何分配/建立裝置與核心通訊所需要的資源。
IRQ線:NIC必須指派一個IRQ,用於在必要時喚起核心的注意(虛擬設備不需要分配IRQ,因為它的工作都是在內部實現。)/proc/interrupts檔案可用於觀察當前中斷線指派狀態。I/O連接埠和記憶體註冊: 驅動程式會將裝置的一塊記憶體映射到系統記憶體,使得驅動程式的讀寫操作能夠通過系統記憶體直接進行。註冊和釋放操作分別由request_region和 release_region進行。
裝置與核心之間的互動幾乎所有裝置與核心的互動都是通過以下兩種方式:
核心的輪詢: 核心定時檢查裝置的狀態,判斷裝置是否有什麼請求。
裝置驅動的插斷要求: 裝置驅動發送硬體訊號引起核心注意
核心輪詢在其他文章會介紹,本文主要介紹硬體中斷中與網路有關的概念。
硬體中斷 每一個中斷都會運行一個中斷處理常式,這些中斷響應程式都是裝置驅動為裝置量身定做的。一般而言,當裝置註冊一個NIC時,它首先會請求並分配一個IRQ,然後要為IRQ註冊(如果裝置被卸載了,則需要登出)一個IRQ響應程式。相應的核心代碼在kernel/irq/manage.c和arch/XXX/kernel/irq.c。(其中XXX為處理器架構)
int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)void free_irq(unsigned int irq, void *dev_id)
注意:irq的註冊和釋放函數都帶有參數dev_id。因為IRQ是可以共用的,因此需要IRQ number和dev_id共同來唯一表示中斷。
另,在註冊IRQ時,必須保證IRQ還未有裝置請求,除非所有裝置都支援IRQ共用。
核心接收到一個中斷訊號時,會通過IRQ number調用關聯的中斷響應程式。IRQ number與中斷響應程式以表的形式儲存。由於多個裝置可能共用IRQ的關係,IRQ number與中斷響應程式的關係可能是一對多的。
中斷類型:接收到資料幀、幀傳輸失敗、DMA傳輸已成功完成、裝置已經有足夠記憶體來建立新的傳輸會話(可用NIC可用記憶體達到一定數值<一般為裝置MTU>時產生一個中斷) 為了防止核心在裝置記憶體不足時多次提交傳輸請求,裝置驅動可以關閉核心出口隊列,待到資源足夠是才重啟。下面是一個範例:
static netdev_tx_tel3_start_xmit(struct sk_buff *skb, struct net_device *dev){ …… netif_stop_queue (dev); …… dev->trans_start = jiffies; if (inw(ioaddr + TX_FREE) > 1536) netif_start_queue(dev); else /* Interrupt us when the FIFO has room for max-sized packet. */ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); ……}
IRQ共用:IRQ線是很有限的資源,為了讓一個系統能支援更多的裝置,只能讓多個裝置共用IRQ線。IRQ共用的機制是這樣的,核心收到插斷要求,然後調用所有與該中斷相關聯的響應常式,然後有各個響應常式自行判斷過濾是否對這個中斷進行處理。(注意,IRQ與響應程式是一對多的,發生一個IRQ,哪些響應程式要處理,哪些不需要不是有核心去判斷,而是各個中斷響應程式自己判斷,核心則是調用所有的響應程式。)
IRQ與IRQ響應程式的組織:用全域的vector:irq_desc來組織,irq_desc包含所有IRQ,每個IRQ對應自己的鏈表,鏈表中是該IRQ關聯的所有響應程式。只有IRQ共用時,IRQ鏈表的節點才會超過一個。整個組織如:
初始化選項 所有的系統內建組件以及作為模組載入的裝置都能通過使用者的輸入參數調整所實現的功能、重寫其預設值,或者在引導前後有不同的值。
模組選項:(module_param系列的宏) 模組載入時可以定義。如果是內建的組件,由於在引導期間無法配置,可以通過sys/進行運行時配置。
引導期間核心選項:(__setup系列宏) 引導期間提供。用於可以內建到核心的
模組。
裝置處理層的初始化 網路代碼初始化有以下重要的部分:流量控制,每個CPU輸入隊列初始化。這些初始化工作在引導期間由net_dev_init完成:
static int __init net_dev_init(void)subsys_initcall(net_dev_init);
使用者空間輔助程式
- /sbin/modprobe 在核心需要載入某個模組時調用,判斷核心傳遞的模組是不是/etc/modprobe.conf檔案中定義的別名
- /sbin/hotplug 在核心檢測到一個新裝置插入或拔出系統時調用,它的任務是根據裝置標識載入正確的驅動
- 以模組方式載入
kmod模組載入器允許核心組件通過調用request_module請求載入某個模組
舉個例子;如果系統管理員使用ifconfig配置某個網卡,但這個網卡驅動還沒有載入,如eth0,核心就會給/sbin/modprobe發送一個請求,讓它載入名稱為
eth0的模組。如果/etc/modprobe.conf中包含“alias eth0 xxx”的字元,/sbin/modprobe就會嘗試載入xxx.ko模組。
module_param 宏定義在引入sysfs後可以通過檔案來訪問得到模組參數
模組選項有三項 , 第一項參數名稱,第二項參數類型,第三項表示參數作為檔案在sys檔案系統中所有的許可權。
每個模組都會在sys/modules下產生對應的目錄,通過目錄下的檔案可以擷取模組參數。
- pnp熱插拔
hotplug允許核心檢測熱插拔裝置的插入和拔出並通知使用者進程(/sbin/hotplug),使用者進程根據這些通知來載入相應的驅動
在編譯核心時,會在kernel目錄下產生modules.pcimap和modules.usbmap兩個檔案,這兩個檔案分別包含了核心所支援裝置的pci id和usb id,檔案中還包
含於每個裝置的id相對應的核心模組名稱,當使用者進程收到核心關於pnp的通知後,會使用這個檔案來尋找正確的裝置驅動
虛擬設備 虛擬設備一般也使用net_device結構體進行執行個體化(也有一些例外,如別名介面裝置)。
虛擬設備一般會有使用者空間組態工具來對其進行配置。尤其是無法使用ifconfig來進行配置的進階欄位。
虛擬設備一般會有一個/proc介面目錄,其內容詳細程度取決於虛擬設備的設計。
虛擬設備與這是裝置的對應關係不是一一對應的。這就導致了虛擬設備可能需要進行流量控制的配置。
虛擬設備的流量是簡介從真實物理裝置獲得的,因而不需要分配IRQ、IO連接埠和IO記憶體。
虛擬設備與其他真實物理裝置一樣,能對特殊的事件通知做出相應的反應。
/proc調整
深入理解Linux網路技術內幕——網路裝置初始化