網絡設備(network device)是內核對網絡適配器(硬件)的抽象與封裝,併爲各個協議實例提供統一的接口,它是硬件與內核的接口,它有兩個特徵:
(1) 做爲基於硬件的網絡適配器與基於軟件的協議之間的接口;
(2) 內核協議棧異步輸入輸出點。
記住:網絡設備軟件對硬件的抽象
網絡設備與協議和網絡適配器的關係以下:
一、 net_device接口(net_device Interface)
網絡設備是內核中除了字符設備、塊設備以外第三類主要設備,它的主要特徵之一就是在設備文件系統/dev/沒有相應的表示,即不存在/dev/eth0等,這就意味着不能經過簡單的讀寫操做來訪問它們。
net_device結構保存與網絡設備相關的全部信息。每個網絡設備都對應一個這樣的結構,包括真實設備(例如以太網卡)和虛擬設備(好比 bonding 或 VLAN)。
全部設備的 net_device 結構都放在一個全局鏈表中,鏈表的頭指針是 dev_base。net_device結構的定義在include/linux/netdevice.h中。與 sk_buff 相似,net_device 結構比較大,並且包含了不少特性相關的參數,這些參數在不一樣的協議層中使用。出於這個緣由,net_device 結構的組織會有一些改變,用於優化協議棧的性能。 網絡設備能夠分爲不一樣的類型,好比以太網卡和令牌環網卡。net_device 結構中的某些變量對同一類型的設備來講, 取值是相同的; 而某些變量在同一設備的不一樣工做模式下,取值必須不一樣。所以,對幾乎全部類型的設備,linux內核提供了一個通用的函數用於初始化那些在全部模式下取值相同的變量。每個設備驅動在調用這個函數的同時,還初始化那些在當前模式下取值不一樣的變量。設備驅動一樣能夠覆蓋那些由內核初始化的變量(例如,在優化設備性能時)。
net_device的定義:node
net_device結構主要分爲如下幾部分:
1.一、 通用字段
name:
網絡適配器的名稱,好比eth0。在註冊網絡設備時能夠爲設備分配一個名稱,便必須惟一。
next:
全部的網絡設備組成一個由dev_base開頭的鏈表。
int ifindex :
全局惟一的設備ID。在每一個設備註冊時,調用dev_new_index 生成。
int iflink:
這個變量主要被(虛擬)隧道設備使用,用於標識隧道的真實設備。
state:
它包含一組被網絡隊列子系統使用的標記。這些標記的值是枚舉類型netdev_state_t中的索引值,這個類型的定義在 include/linux/netdevice.h 中,每一個標記都是諸如__LINK_STATE_XOFF 這樣的常量。每一位均可以經過函數 set_bit 和 clear_bit 設置或清除,但一般狀況下,都會有一個包裝函數來隱藏標記位的信息。例如,在網絡隊列子系統中止一個設備隊列時,它調用函數 netif_stop_queue,這個函數的定義以下:
static inline void netif_stop_queue(struct net_device *dev)
{
...
set_bit(_ _LINK_STATE_XOFF, &dev->state);
}
trans_start:
最後一個幀開始發送的時間(用jiffies度量)。設備驅動在發送以前設置這個變量。這個變量用來檢測網卡是否在給定的時間內把幀發送了出去。 太長的發送時間意味
着有錯誤發生,在這種狀況下,設備驅動會重置網卡。
last_rx :
接收到最後一個包的時間(用jiffies度量)。
xmit_lock 和xmit_lock_owner :
xmit_lock 用來序列化對設備驅動函數hard_start_xmit的調用。這意味着,每一個cpu每次只能調用設備完成一次發送。xmit_lock_owner 是擁有鎖的 CPU 的 ID。在單cpu 系統上,這個值是 0;在多 cpu 系統中,若是鎖沒有被佔用,這個值是-1。內核一樣容許不加鎖的發送,前提條件是設備驅動必須支持這個功能。
struct hlist_node name_hlist
struct hlist_node index_hlist
把net_device結構連接到兩個哈希表中。
1.二、 硬件相關
unsigned int irq
設備中斷號。它能夠被多個設備共享。設備驅動調用request_irq來分配這個值,並
調用free_irq來釋放它。
unsigned char if_port
接口的端口類型。有些設備能夠支持多種接口(最多見的組合是 BNC+RJ45),用戶能夠根據須要來選擇使用哪一種接口。這個變量用來設置設備的接口類型。若是配置命令沒有指定設備的接口類型,設備驅動就使用缺省的類型。在某些狀況下,一個設備驅動能夠處理多種接口類型;在這種狀況下,設備驅動能夠按必定的順序來測試每一個接口的類型。下面的代碼片段展現了一個設備驅動如何根據配置來設置接口的類型:
switch (dev->if_port) {
case IF_PORT_10BASE2:
writeb((readb(addr) & 0xf8) | 1, addr);
break;
case IF_PORT_10BASET:
writeb((readb(addr) & 0xf8), addr);
break;
}
unsigned char dma
設備所使用的 DMA 通道。爲獲取和釋放一個 DMA 通道,內核在 kernel/dma.c 中定義了兩個函數request_dma和free_dma。爲了在獲取dma通道後,啓用或者中止dma通道,內核定義了兩個函數enable_dma和disable_dma。這兩個函數的實現與
體系結構相關,因此在 include/asm-architecture 下有相關的文件(例如include/asm-i386)。這些函數被 ISA 設備使用;PCI 設備不使用這些函數,它們使
用其餘函數。並非全部的設備均可以使用dma,由於有些總線不支持dma。
unsigned long mem_start
unsigned long mem_end
這兩個變量描述設備與內核通訊所用到的內存邊界。它們由設備驅動初始化,而且只能被設備驅動訪問;高層協議不須要關心這塊內存。
unsigned long base_addr
映射到設備內存空間中I/O 內存起始地址。
1.三、 物理層相關
unsigned mtu
MTU 的意思是最大傳輸單元,它表示設備能夠處理幀的最大長度。不一樣設備的MTU值:
unsigned short type
設備類型(以太網,幀中繼等)。在include/linux/if_arp.h 中有完整的類型列表。
unsigned short hard_header_len
以字節爲單位的幀頭部長度。例如,以太網幀的頭是 14 字節。某種設備所支持幀的頭部長度在相應的設備頭文件中定義。對以太網來講,ETH_HLEN 在
<include/linux/if_ether.h>中定義。
unsigned char broadcast[MAX_ADDR_LEN]
鏈路層廣播地址。
unsigned char dev_addr[MAX_ADDR_LEN]
unsigned char addr_len
dev_addr是設備的鏈路層地址,不要把它和IP 地址或者L3 地址混淆了。鏈路層地址的長度是 addr_len,以字節爲單位。addr_len 的大小與設備類型有關。以太網地址的長度是8。
int promiscuity
promiscuity計數器來標識設備是否工做在混雜模式。之因此使用計數器而不是一個標誌位的緣由是:可能有多個用戶程序設置設備工做在混雜模式下。所以,每次進入混雜模式,計數器加一;退出混雜模式,計數器減一。只有計數器爲0 時,設備才退出混雜模式。這個變量一般調用函數dev_set_promiscuity 來設置。
struct dev_mc_list *mc_list
指向dev_mc_list結構
int mc_count
設備多播地址的數量,它一樣表示mc_list所指向鏈表的長度。
int allmulti
若是是非零值,那麼設備將監聽全部的多播地址。和 promiscuity 同樣,這個變量是一個計數器而不只僅是一個布爾值。這是由於多個設備(好比VLAN和bonding
設備)可能獨立地要求監聽全部地址。若是這個變量的值從0變爲非零,內核會調用函數dev_set_allmulti通知設備監聽全部的多播地址。若是這個值變爲0,則中止監聽全部的多播地址。
1.四、 協議相關
void *atalk_ptr
void *ip_ptr
void *dn_ptr
void *ip6_ptr
void *ec_ptr
void *ax25_ptr
這六個變量指向特定協議的數據結構,每一個數據結構都包含協議私有的參數。例如,ip_ptr 指向一個 in_device 類型的結構(儘管 ip_ptr 的類型是 void*),它包含 IPv4相關的參數,其中包括設備的 IP 地址列表等。
1.五、 流量管理
Linux 流量控制子系統的功能已經很是強大,而且已經成爲 Linux 內核中的一個重要組件。相關的內核選項是 「Device drivers ->Networking support ->Networking options ->QoS and/or fair queueing」。net_device中的相關變量包括:
struct net_device *next_sched
被內核軟中斷使用。
struct Qdisc *qdisc
struct Qdisc *qdisc_sleeping
struct Qdisc *qdisc_ingress
struct list_head qdisc_list
這些變量管理設備的接收,發送隊列,而且能夠被不一樣的cpu訪問。
spinlock_t queue_lock
spinlock_t ingress_lock
流量控制子系統爲每一個網絡設備定義了一個私有的發送隊列。 queue_lock用於避免併發的訪問(參見第11章)。ingress_lock 用於保護接收隊列。
unsigned long tx_queue_len
設備發送隊列的長度。若是內核中包含了流量控制子系統,這個變量可能沒有什麼用(只有幾個排隊策略會使用它)。常見設備的 tx_queue_len 值(這個值能夠經過sysfs文件系統修改(在/sys/class/net/device_name/目錄下)):
1.六、 設備驅動程序相關
int (*init)(...)
void (*uninit)(...)
void (*destructor)(...)
int (*open)(...)
int (*stop)(...)
用於初始化,清除,銷燬,啓用和中止一個設備。這些函數並非每一個設備都會用到。
int (*hard_start_xmit)(...)
發送一個幀。
int (*hard_header)(...)
根據源和目標的第2層地址建立第2層報文頭。
int (*rebuild_header)(...)
負責在傳送包以前重建第2導報文頭。
int (*set_mac_address)(...)
修改設備的 MAC 地址。若是設備不提供這個功能(好比網橋設備),能夠把這個指針設置爲NULL。
int (*change_mtu)(...)
修改設備的MTU,修改mtu 不會對設備驅動有任何影響,它只是讓協議棧軟件能夠根據新的mtu 正確地處理分片。linux