1. 程式人生 > >深入理解Linux網路技術內幕——裝置的註冊與初始化(一)

深入理解Linux網路技術內幕——裝置的註冊與初始化(一)

副標題:設備註冊相關的基本結構的原理框架

設備註冊與刪除時間

裝置在下列兩種情況下進行註冊: 1)載入NIC驅動時 2)插入熱插拔裝置時 這裡NIC與熱插拔裝置有些不同。a.對於非熱插拔NIC來說,NIC的註冊是伴隨著其驅動的發生的,而NIC可以內建到核心,也可以作為模組載入,如果內建入核心,則NIC裝置和初始化均發生在引導時,如果NIC作為模組載入,則NIC的註冊和驅動初始化均發生在模組載入時。b. 對於熱插拔NIC裝置來說,其驅動已經載入,因此裝置的註冊發生在插入裝置,核心通知關聯驅動時。     裝置的登出主要發生在下面兩種情況
1)解除安裝NIC驅動時(僅適用於以模組形式載入的裝置)
2)拔出熱插拔裝置時

裝置的註冊於登出架構

    下圖分別描述了網路裝置的註冊和登出的大致流程,(以PCI Ethernet NIC為例,其它網路類似,主要是名字差異)
    首先呼叫alloc_etherdev(即alloc_netdev的包裹函式),alloc_etherdev會為所有Ethernet裝置通用的引數做初始化,驅動程式會初始化net_device的另一部分。之後以register_netdev完成註冊。

net_device

分配net_device

net_device由alloc_netdev分配空間
//include/linux/netdevice.h
#define alloc_netdev(sizeof_priv, name, setup) \
    alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)  
//net/core/dev.c
struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
        void (*setup)(struct net_device *),
        unsigned int txqs, unsigned int rxqs)
alloc_netdev帶三個引數: sizeof_priv: 因為net_device可以由驅動程式拓展私有空間,此引數表示拓展的私有空間的大小
name:裝置名字(名字的一部分,可能還有一部分由核心完成) setup:用於初始化裝置的一些引數 此處裝置會被分配一個裝置名字,改名字一般帶有序號,以確保唯一性:如eth0,eth1
alloc_netdev一般會有一些包裹函式對其進行包裹:

裝置(net_device)初始化

net_device結構非常龐大,其成員會有不同函式分批進行初始化。每個函式分別負責一組不同的欄位子集。特別是下面三部分:
裝置驅動程式:
    如IRQ、IO記憶體及IO埠,其值取決於硬體配置,有裝置驅動程式進行初始化。
裝置型別:     同一裝置型別的通用欄位進行初始化,由XXX_setup進行。(裝置型別初始話屬於裝置驅動程式初始化一部分,XXX_setup有XXX_probe呼叫) 各種功能:     一部分在register_device中進行,有一些在關聯模組接到通知時進行。

net_device結構的組織

alloc_netdev第一個引數知名了私有資料塊的大小,因此私有資料塊會附載入net_device結構後。即net_device可以分為兩部分,一部分是net_device基礎的資料,第二部分是私有資料塊(如下圖所以)。私有資料塊的空間分配可以與net_device第一部分資料空間一起完成(如驅動程式A、B),也可以有驅動程式自己分配(如驅動程式C)。     從圖中可以看出,net_device空間分配是,會有一塊對其區域塊(alignment padding),因此dev_base(全域性表的表頭指標)以及net_deivce中的next需要指向結構的開頭,而非分配區域塊的開頭(即跳過填充區域塊)。
    net_device插入在一個全域性列表(以dev_base開頭)和兩張雜湊表中後面介紹(以dev_name_head和dev_index_head開頭)
dev_base:     全域性表,內含所有net_device裝置,能讓核心輕易的瀏覽所有裝置。
dev_name_head:     hash表,以裝置名稱為索引,在使用老一代配置工具ioctl介面時極為有用。 dev_index_head:     hash表,以裝置ID:dev->ifindex為索引。使用新一代配置工具ip(來自IPROUTER2套件時),ip工具會通過Netlink與核心互動,這是通常就以裝置ID為索引了。 查詢:     一般由 dev_get_by_name 和dev_get_by_index通過上面兩張Hash表實現。有可能根據裝置型別和mac地址通過全域性表dev_base列表實現。 所有查詢都由 dev_base_lock鎖進行保護 All lookup routines are defined innet/core/dev.c.

裝置狀態

net_dev有多個欄位用於定義裝置當前狀態:
flags     //用於各種標識的位域,多數標識代表裝置擁有的能力,但IFF_UP特殊,標識裝置開啟或關閉,參見uapi/linux/if.h(IFF_XXX定義),並參考下文"開啟和關閉網路裝置“
reg_state //設備註冊狀態,參見下文註冊狀態
state     //和其他裝置有關的裝置狀態,參見下文“佇列規則狀態”

設備註冊狀態:
NETREG_UNINITIALIZED //定義為0,net_device已分配, 且內容清0
NETREG_REGISTERING    //net_device已新增到“net_device結構的組織”提及的結構內,但核心依然要想/sys新增專案
NETREG_REGISTERED     //設備註冊完成
NETREG_UNREGISTERING  //net_device已由“net_device結構的組織”提及的結構內刪除
NETREG_UNREGISTERED   //裝置完全除名
NETREG_RELEASED       //所有對net_device結構的引用已釋放

佇列規則狀態:
_ _LINK_STATE_START      //裝置開啟
_ _LINK_STATE_PRESENT    //裝置存在
_ _LINK_STATE_NOCARRIER  //沒有載波
_ _LINK_STATE_LINKWATCH_EVENT  //裝置狀態已變更
_ _LINK_STATE_XOFF             //以下三個標識由負責管理流量入口與出口的裝置所使用
_ _LINK_STATE_SHED             //
_ _LINK_STATE_RX_SCHED         //

虛擬裝置:

    虛擬裝置與真實裝置有幾個地方有差異:     虛擬裝置偶爾會呼叫register_netdevice和unregister_netdevice,而不是其包裹函式。並且自行上鎖、解鎖,以便獲得更多的持有鎖的時間。     真實裝置不能由使用者命令除名,而在解除安裝時自己除名。虛擬裝置可以通過使用者命令除名,但除名是否成功依賴於虛擬裝置驅動設計。     虛擬裝置本文就不太多介紹了,以後再單獨補充一篇虛擬裝置與真實裝置的差異的博文

上鎖:

    net_devices包括一些上鎖的手段,以後再補充了。。。