Linux网络协议栈(三)——网络设备(1)
生活随笔
收集整理的這篇文章主要介紹了
Linux网络协议栈(三)——网络设备(1)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
網(wǎng)絡設備(network device)是內(nèi)核對網(wǎng)絡適配器(硬件)的抽象與封裝,并為各個協(xié)議實例提供統(tǒng)一的接口,它是硬件與內(nèi)核的接口,它有兩個特征:
(1)?? ?作為基于硬件的網(wǎng)絡適配器與基于軟件的協(xié)議之間的接口;
(2)?? ?內(nèi)核協(xié)議棧異步輸入輸出點。
記住:網(wǎng)絡設備軟件對硬件的抽象
網(wǎng)絡設備與協(xié)議和網(wǎng)絡適配器的關(guān)系如下:
1、?? ?net_device接口(net_device Interface)
???? 網(wǎng)絡設備是內(nèi)核中除了字符設備、塊設備之外第三類主要設備,它的主要特征之一就是在設備文件系統(tǒng)/dev/沒有相應的表示,即不存在/dev/eth0等,這就意味著不能通過簡單的讀寫操作來訪問它們。
??? net_device結(jié)構(gòu)保存與網(wǎng)絡設備相關(guān)的所有信息。每一個網(wǎng)絡設備都對應一個這樣的結(jié)構(gòu),包括真實設備(例如以太網(wǎng)卡)和虛擬設備(比如 bonding 或 VLAN)。
所有設備的 net_device 結(jié)構(gòu)都放在一個全局鏈表中,鏈表的頭指針是 dev_base。net_device結(jié)構(gòu)的定義在include/linux/netdevice.h中。與 sk_buff 類似,net_device 結(jié)構(gòu)比較大,而且包含了很多特性相關(guān)的參數(shù),這些參數(shù)在不同的協(xié)議層中使用。出于這個原因,net_device 結(jié)構(gòu)的組織會有一些改變,用于優(yōu)化協(xié)議棧的性能。 網(wǎng)絡設備可以分為不同的類型,比如以太網(wǎng)卡和令牌環(huán)網(wǎng)卡。net_device 結(jié)構(gòu)中的某些變量對同一類型的設備來說, 取值是相同的; 而某些變量在同一設備的不同工作模式下,取值必須不同。因此,對幾乎所有類型的設備,linux內(nèi)核提供了一個通用的函數(shù)用于初始化那些在所有模式下取值相同的變量。每一個設備驅(qū)動在調(diào)用這個函數(shù)的同時,還初始化那些在當前模式下取值不同的變量。設備驅(qū)動同樣可以覆蓋那些由內(nèi)核初始化的變量(例如,在優(yōu)化設備性能時)。
net_device的定義:
Code
//include/linux/netdevice.h
/*
?*????The?DEVICE?structure.
?*????Actually,?this?whole?structure?is?a?big?mistake.??It?mixes?I/O
?*????data?with?strictly?"high-level"?data,?and?it?has?to?know?about
?*????almost?every?data?structure?used?in?the?INET?module.
?*
?*????FIXME:?cleanup?struct?net_device?such?that?network?protocol?info
?*????moves?out.
?*/
struct?net_device
{
????/*
?????*?This?is?the?first?field?of?the?"visible"?part?of?this?structure
?????*?(i.e.?as?seen?by?users?in?the?"Space.c"?file).??It?is?the?name
?????*?the?interface.
?????*/
????char????????????name[IFNAMSIZ];//網(wǎng)絡設備名稱
????/*
?????*????I/O?specific?fields
?????*????FIXME:?Merge?these?and?struct?ifmap?into?one
?????*/
????unsigned?long????????mem_end;????/*?shared?mem?end????*/
????unsigned?long????????mem_start;????/*?shared?mem?start????*/
????unsigned?long????????base_addr;????/*?device?I/O?address????*/
????unsigned?int????????irq;????????/*?device?IRQ?number????*/
????/*
?????*????Some?hardware?also?needs?these?fields,?but?they?are?not
?????*????part?of?the?usual?set?specified?in?Space.c.
?????*/
????unsigned?char????????if_port;????/*?Selectable?AUI,?TP,..*/
????unsigned?char????????dma;????????/*?DMA?channel????????*/
????unsigned?long????????state;
????struct?net_device????*next;
????
????/*?The?device?initialization?function.?Called?only?once.?*/
????int????????????(*init)(struct?net_device?*dev);
????/*?-------?Fields?preinitialized?in?Space.c?finish?here?-------?*/
????struct?net_device????*next_sched;
????/*?Interface?index.?Unique?device?identifier????*/
????int????????????ifindex;
????int????????????iflink;
????struct?net_device_stats*?(*get_stats)(struct?net_device?*dev);
????struct?iw_statistics*????(*get_wireless_stats)(struct?net_device?*dev);
????/*?List?of?functions?to?handle?Wireless?Extensions?(instead?of?ioctl).
?????*?See?<net/iw_handler.h>?for?details.?Jean?II?*/
????const?struct?iw_handler_def?*????wireless_handlers;
????/*?Instance?data?managed?by?the?core?of?Wireless?Extensions.?*/
????struct?iw_public_data?*????wireless_data;
????struct?ethtool_ops?*ethtool_ops;
????/*
?????*?This?marks?the?end?of?the?"visible"?part?of?the?structure.?All
?????*?fields?hereafter?are?internal?to?the?system,?and?may?change?at
?????*?will?(read:?may?be?cleaned?up?at?will).
?????*/
????/*?These?may?be?needed?for?future?network-power-down?code.?*/
????unsigned?long????????trans_start;????/*?Time?(in?jiffies)?of?last?Tx????*/
????unsigned?long????????last_rx;????/*?Time?of?last?Rx????*/
????unsigned?short????????flags;????/*?interface?flags?(a?la?BSD)????*/
????unsigned?short????????gflags;
????????unsigned?short??????????priv_flags;?/*?Like?'flags'?but?invisible?to?userspace.?*/
????????unsigned?short??????????unused_alignment_fixer;?/*?Because?we?need?priv_flags,
?????????????????????????????????????????????????????????*?and?we?want?to?be?32-bit?aligned.
?????????????????????????????????????????????????????????*/
????unsigned????????mtu;????/*?interface?MTU?value????????*/
????unsigned?short????????type;????/*?interface?hardware?type????*/
????unsigned?short????????hard_header_len;????/*?hardware?hdr?length????*/
????void????????????*priv;????/*?pointer?to?private?data????*/
????struct?net_device????*master;?/*?Pointer?to?master?device?of?a?group,
??????????????????????*?which?this?device?is?member?of.
??????????????????????*/
????/*?Interface?address?info.?*/
????unsigned?char????????broadcast[MAX_ADDR_LEN];????/*?hw?bcast?add????*/
????unsigned?char????????dev_addr[MAX_ADDR_LEN];????/*?hw?address????*/
????unsigned?char????????addr_len;????/*?hardware?address?length????*/
????unsigned?short??????????dev_id;????????/*?for?shared?network?cards?*/
????struct?dev_mc_list????*mc_list;????/*?Multicast?mac?addresses????*/
????int????????????mc_count;????/*?Number?of?installed?mcasts????*/
????int????????????promiscuity;
????int????????????allmulti;
????int????????????watchdog_timeo;
????struct?timer_list????watchdog_timer;
????/*?Protocol?specific?pointers?*/
????
????void?????????????*atalk_ptr;????/*?AppleTalk?link?????*/
????void????????????*ip_ptr;????/*?IPv4?specific?data????*/??
????void????????????????????*dn_ptr;????????/*?DECnet?specific?data?*/
????void????????????????????*ip6_ptr;???????/*?IPv6?specific?data?*/
????void????????????*ec_ptr;????/*?Econet?specific?data????*/
????void????????????*ax25_ptr;????/*?AX.25?specific?data?*/
????struct?list_head????poll_list;????/*?Link?to?poll?list????*/
????int????????????quota;
????int????????????weight;
????struct?Qdisc????????*qdisc;
????struct?Qdisc????????*qdisc_sleeping;
????struct?Qdisc????????*qdisc_ingress;
????struct?list_head????qdisc_list;
????unsigned?long????????tx_queue_len;????/*?Max?frames?per?queue?allowed?*/
????/*?ingress?path?synchronizer?*/
????spinlock_t????????ingress_lock;
????/*?hard_start_xmit?synchronizer?*/
????spinlock_t????????xmit_lock;
????/*?cpu?id?of?processor?entered?to?hard_start_xmit?or?-1,
???????if?nobody?entered?there.
?????*/
????int????????????xmit_lock_owner;
????/*?device?queue?lock?*/
????spinlock_t????????queue_lock;
????/*?Number?of?references?to?this?device?*/
????atomic_t????????refcnt;
????/*?delayed?register/unregister?*/
????struct?list_head????todo_list;
????/*?device?name?hash?chain?*/
????struct?hlist_node????name_hlist;
????/*?device?index?hash?chain?*/
????struct?hlist_node????index_hlist;
????/*?register/unregister?state?machine?*/
????enum?{?NETREG_UNINITIALIZED=0,
???????????NETREG_REGISTERING,????/*?called?register_netdevice?*/
???????????NETREG_REGISTERED,????/*?completed?register?todo?*/
???????????NETREG_UNREGISTERING,????/*?called?unregister_netdevice?*/
???????????NETREG_UNREGISTERED,????/*?completed?unregister?todo?*/
???????????NETREG_RELEASED,????????/*?called?free_netdev?*/
????}?reg_state;
????/*?Net?device?features?*/
????int????????????features;
#define?NETIF_F_SG????????1????/*?Scatter/gather?IO.?*/
#define?NETIF_F_IP_CSUM????????2????/*?Can?checksum?only?TCP/UDP?over?IPv4.?*/
#define?NETIF_F_NO_CSUM????????4????/*?Does?not?require?checksum.?F.e.?loopack.?*/
#define?NETIF_F_HW_CSUM????????8????/*?Can?checksum?all?the?packets.?*/
#define?NETIF_F_HIGHDMA????????32????/*?Can?DMA?to?high?memory.?*/
#define?NETIF_F_FRAGLIST????64????/*?Scatter/gather?IO.?*/
#define?NETIF_F_HW_VLAN_TX????128????/*?Transmit?VLAN?hw?acceleration?*/
#define?NETIF_F_HW_VLAN_RX????256????/*?Receive?VLAN?hw?acceleration?*/
#define?NETIF_F_HW_VLAN_FILTER????512????/*?Receive?filtering?on?VLAN?*/
#define?NETIF_F_VLAN_CHALLENGED????1024????/*?Device?cannot?handle?VLAN?packets?*/
#define?NETIF_F_TSO????????2048????/*?Can?offload?TCP/IP?segmentation?*/
#define?NETIF_F_LLTX????????4096????/*?LockLess?TX?*/
????/*?Called?after?device?is?detached?from?network.?*/
????void????????????(*uninit)(struct?net_device?*dev);
????/*?Called?after?last?user?reference?disappears.?*/
????void????????????(*destructor)(struct?net_device?*dev);
????/*?Pointers?to?interface?service?routines.????*/
????int????????????(*open)(struct?net_device?*dev);
????int????????????(*stop)(struct?net_device?*dev);
????int????????????(*hard_start_xmit)?(struct?sk_buff?*skb,
????????????????????????????struct?net_device?*dev);
#define?HAVE_NETDEV_POLL
????int????????????(*poll)?(struct?net_device?*dev,?int?*quota);
????int????????????(*hard_header)?(struct?sk_buff?*skb,
????????????????????????struct?net_device?*dev,
????????????????????????unsigned?short?type,
????????????????????????void?*daddr,
????????????????????????void?*saddr,
????????????????????????unsigned?len);
????int????????????(*rebuild_header)(struct?sk_buff?*skb);
#define?HAVE_MULTICAST?????????????
????void????????????(*set_multicast_list)(struct?net_device?*dev);
#define?HAVE_SET_MAC_ADDR???????????
????int????????????(*set_mac_address)(struct?net_device?*dev,
???????????????????????????void?*addr);
#define?HAVE_PRIVATE_IOCTL
????int????????????(*do_ioctl)(struct?net_device?*dev,
????????????????????????struct?ifreq?*ifr,?int?cmd);
#define?HAVE_SET_CONFIG
????int????????????(*set_config)(struct?net_device?*dev,
??????????????????????????struct?ifmap?*map);
#define?HAVE_HEADER_CACHE
????int????????????(*hard_header_cache)(struct?neighbour?*neigh,
?????????????????????????????struct?hh_cache?*hh);
????void????????????(*header_cache_update)(struct?hh_cache?*hh,
???????????????????????????????struct?net_device?*dev,
???????????????????????????????unsigned?char?*??haddr);
#define?HAVE_CHANGE_MTU
????int????????????(*change_mtu)(struct?net_device?*dev,?int?new_mtu);
#define?HAVE_TX_TIMEOUT
????void????????????(*tx_timeout)?(struct?net_device?*dev);
????void????????????(*vlan_rx_register)(struct?net_device?*dev,
????????????????????????????struct?vlan_group?*grp);
????void????????????(*vlan_rx_add_vid)(struct?net_device?*dev,
???????????????????????????unsigned?short?vid);
????void????????????(*vlan_rx_kill_vid)(struct?net_device?*dev,
????????????????????????????unsigned?short?vid);
????int????????????(*hard_header_parse)(struct?sk_buff?*skb,
?????????????????????????????unsigned?char?*haddr);
????int????????????(*neigh_setup)(struct?net_device?*dev,?struct?neigh_parms?*);
????int????????????(*accept_fastpath)(struct?net_device?*,?struct?dst_entry*);
#ifdef?CONFIG_NETPOLL
????int????????????netpoll_rx;
#endif
#ifdef?CONFIG_NET_POLL_CONTROLLER
????void????????????????????(*poll_controller)(struct?net_device?*dev);
#endif
????/*?bridge?stuff?*/
????struct?net_bridge_port????*br_port;
#ifdef?CONFIG_NET_DIVERT
????/*?this?will?get?initialized?at?each?interface?type?init?routine?*/
????struct?divert_blk????*divert;
#endif?/*?CONFIG_NET_DIVERT?*/
????/*?class/net/name?entry?*/
????struct?class_device????class_dev;
????/*?how?much?padding?had?been?added?by?alloc_netdev()?*/
????int?padded;
};
??? net_device結(jié)構(gòu)主要分為以下幾部分:
1.1、?? ?通用字段
name:
網(wǎng)絡適配器的名稱,比如eth0。在注冊網(wǎng)絡設備時可以為設備分配一個名稱,便必須唯一。
next:
所有的網(wǎng)絡設備組成一個由dev_base開頭的鏈表。
int ifindex :
全局唯一的設備ID。在每個設備注冊時,調(diào)用dev_new_index 生成。
int iflink:
這個變量主要被(虛擬)隧道設備使用,用于標識隧道的真實設備。
state:
它包含一組被網(wǎng)絡隊列子系統(tǒng)使用的標記。這些標記的值是枚舉類型netdev_state_t中的索引值,這個類型的定義在 include/linux/netdevice.h 中,每個標記都是諸如__LINK_STATE_XOFF 這樣的常量。每一位都可以通過函數(shù) set_bit 和 clear_bit 設置或清除,但通常情況下,都會有一個包裝函數(shù)來隱藏標記位的信息。例如,在網(wǎng)絡隊列子系統(tǒng)停止一個設備隊列時,它調(diào)用函數(shù) netif_stop_queue,這個函數(shù)的定義如下:
? static inline void netif_stop_queue(struct net_device *dev)
{
??????? ...
??????? set_bit(_ _LINK_STATE_XOFF, &dev->state);
}
trans_start:
最后一個幀開始發(fā)送的時間(用jiffies度量)。設備驅(qū)動在發(fā)送之前設置這個變量。這個變量用來檢測網(wǎng)卡是否在給定的時間內(nèi)把幀發(fā)送了出去。 太長的發(fā)送時間意味
著有錯誤發(fā)生,在這種情況下,設備驅(qū)動會重置網(wǎng)卡。
last_rx :
接收到最后一個包的時間(用jiffies度量)。
xmit_lock 和xmit_lock_owner :
xmit_lock 用來序列化對設備驅(qū)動函數(shù)hard_start_xmit的調(diào)用。這意味著,每個cpu每次只能調(diào)用設備完成一次發(fā)送。xmit_lock_owner 是擁有鎖的 CPU 的 ID。在單cpu 系統(tǒng)上,這個值是 0;在多 cpu 系統(tǒng)中,如果鎖沒有被占用,這個值是-1。內(nèi)核同樣允許不加鎖的發(fā)送,前提條件是設備驅(qū)動必須支持這個功能。
struct hlist_node name_hlist
struct hlist_node index_hlist
? 把net_device結(jié)構(gòu)鏈接到兩個哈希表中。
1.2、?? ?硬件相關(guān)
unsigned int irq
? 設備中斷號。它可以被多個設備共享。設備驅(qū)動調(diào)用request_irq來分配這個值,并
調(diào)用free_irq來釋放它。
unsigned char if_port
? 接口的端口類型。有些設備可以支持多種接口(最常見的組合是 BNC+RJ45),用戶可以根據(jù)需要來選擇使用哪種接口。這個變量用來設置設備的接口類型。如果配置命令沒有指定設備的接口類型,設備驅(qū)動就使用缺省的類型。在某些情況下,一個設備驅(qū)動可以處理多種接口類型;在這種情況下,設備驅(qū)動可以按一定的順序來測試每個接口的類型。下面的代碼片斷展示了一個設備驅(qū)動如何根據(jù)配置來設置接口的類型:
? 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 通道,內(nèi)核在 kernel/dma.c 中定義了兩個函數(shù)request_dma和free_dma。為了在獲取dma通道后,啟用或者停止dma通道,內(nèi)核定義了兩個函數(shù)enable_dma和disable_dma。這兩個函數(shù)的實現(xiàn)與
體系結(jié)構(gòu)相關(guān),所以在 include/asm-architecture 下有相關(guān)的文件(例如include/asm-i386)。這些函數(shù)被 ISA 設備使用;PCI 設備不使用這些函數(shù),它們使
用其他函數(shù)。并不是所有的設備都可以使用dma,因為有些總線不支持dma。
?unsigned long mem_start
unsigned long mem_end
? 這兩個變量描述設備與內(nèi)核通信所用到的內(nèi)存邊界。它們由設備驅(qū)動初始化,并且只能被設備驅(qū)動訪問;高層協(xié)議不需要關(guān)心這塊內(nèi)存。
unsigned long base_addr
? 映射到設備內(nèi)存空間中I/O 內(nèi)存起始地址。
1.3、?? ?物理層相關(guān)
unsigned mtu
? MTU 的意思是最大傳輸單元,它表示設備可以處理幀的最大長度。不同設備的MTU值:
unsigned short type
??? 設備類型(以太網(wǎng),幀中繼等)。在include/linux/if_arp.h 中有完整的類型列表。
unsigned short hard_header_len
? 以字節(jié)為單位的幀頭部長度。例如,以太網(wǎng)幀的頭是 14 字節(jié)。某種設備所支持幀的頭部長度在相應的設備頭文件中定義。對以太網(wǎng)來說,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,以字節(jié)為單位。addr_len 的大小與設備類型有關(guān)。以太網(wǎng)地址的長度是8。
int promiscuity
promiscuity計數(shù)器來標識設備是否工作在混雜模式。之所以使用計數(shù)器而不是一個標志位的原因是:可能有多個用戶程序設置設備工作在混雜模式下。因此,每次進入混雜模式,計數(shù)器加一;退出混雜模式,計數(shù)器減一。只有計數(shù)器為0 時,設備才退出混雜模式。這個變量通常調(diào)用函數(shù)dev_set_promiscuity 來設置。
struct dev_mc_list *mc_list
? 指向dev_mc_list結(jié)構(gòu)
int mc_count
? 設備多播地址的數(shù)量,它同樣表示mc_list所指向鏈表的長度。
int allmulti
? 如果是非零值,那么設備將監(jiān)聽所有的多播地址。和 promiscuity 一樣,這個變量是一個計數(shù)器而不僅僅是一個布爾值。這是因為多個設備(比如VLAN和bonding
設備)可能獨立地要求監(jiān)聽所有地址。如果這個變量的值從0變?yōu)榉橇?#xff0c;內(nèi)核會調(diào)用函數(shù)dev_set_allmulti通知設備監(jiān)聽所有的多播地址。如果這個值變?yōu)?,則停止監(jiān)聽所有的多播地址。
1.4、?? ?協(xié)議相關(guān)
void *atalk_ptr
void *ip_ptr
void *dn_ptr
void *ip6_ptr
void *ec_ptr
void *ax25_ptr
這六個變量指向特定協(xié)議的數(shù)據(jù)結(jié)構(gòu),每個數(shù)據(jù)結(jié)構(gòu)都包含協(xié)議私有的參數(shù)。例如,ip_ptr 指向一個 in_device 類型的結(jié)構(gòu)(盡管 ip_ptr 的類型是 void*),它包含 IPv4相關(guān)的參數(shù),其中包括設備的 IP 地址列表等。
1.5、?? ?流量管理
Linux 流量控制子系統(tǒng)的功能已經(jīng)非常強大,并且已經(jīng)成為 Linux 內(nèi)核中的一個重要組件。相關(guān)的內(nèi)核選項是 “Device drivers ->Networking support ->Networking options ->QoS and/or fair queueing”。net_device中的相關(guān)變量包括:
struct net_device *next_sched
? 被內(nèi)核軟中斷使用。
struct Qdisc *qdisc
struct Qdisc *qdisc_sleeping
struct Qdisc *qdisc_ingress
struct list_head qdisc_list
? 這些變量管理設備的接收,發(fā)送隊列,并且可以被不同的cpu訪問。
spinlock_t queue_lock
spinlock_t ingress_lock
? 流量控制子系統(tǒng)為每個網(wǎng)絡設備定義了一個私有的發(fā)送隊列。 queue_lock用于避免并發(fā)的訪問(參見第11章)。ingress_lock 用于保護接收隊列。
unsigned long tx_queue_len
? 設備發(fā)送隊列的長度。如果內(nèi)核中包含了流量控制子系統(tǒng),這個變量可能沒有什么用(只有幾個排隊策略會使用它)。常見設備的 tx_queue_len 值(這個值可以通過sysfs文件系統(tǒng)修改(在/sys/class/net/device_name/目錄下)):
1.6、?? ?設備驅(qū)動程序相關(guān)
int (*init)(...)
void (*uninit)(...)
void (*destructor)(...)
int (*open)(...)
int (*stop)(...)
用于初始化,清除,銷毀,啟用和停止一個設備。這些函數(shù)并不是每個設備都會用到。
int (*hard_start_xmit)(...)
?發(fā)送一個幀。
int (*hard_header)(...)
?根據(jù)源和目標的第2層地址創(chuàng)建第2層報文頭。
int (*rebuild_header)(...)
?負責在傳送包之前重建第2導報文頭。
int (*set_mac_address)(...)
? 修改設備的 MAC 地址。如果設備不提供這個功能(比如網(wǎng)橋設備),可以把這個指針設置為NULL。
int (*change_mtu)(...)
? 修改設備的MTU,修改mtu 不會對設備驅(qū)動有任何影響,它只是讓協(xié)議棧軟件可以根據(jù)新的mtu 正確地處理分片。 新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎!定制產(chǎn)品紅包拿不停!
(1)?? ?作為基于硬件的網(wǎng)絡適配器與基于軟件的協(xié)議之間的接口;
(2)?? ?內(nèi)核協(xié)議棧異步輸入輸出點。
記住:網(wǎng)絡設備軟件對硬件的抽象
網(wǎng)絡設備與協(xié)議和網(wǎng)絡適配器的關(guān)系如下:
1、?? ?net_device接口(net_device Interface)
???? 網(wǎng)絡設備是內(nèi)核中除了字符設備、塊設備之外第三類主要設備,它的主要特征之一就是在設備文件系統(tǒng)/dev/沒有相應的表示,即不存在/dev/eth0等,這就意味著不能通過簡單的讀寫操作來訪問它們。
??? net_device結(jié)構(gòu)保存與網(wǎng)絡設備相關(guān)的所有信息。每一個網(wǎng)絡設備都對應一個這樣的結(jié)構(gòu),包括真實設備(例如以太網(wǎng)卡)和虛擬設備(比如 bonding 或 VLAN)。
所有設備的 net_device 結(jié)構(gòu)都放在一個全局鏈表中,鏈表的頭指針是 dev_base。net_device結(jié)構(gòu)的定義在include/linux/netdevice.h中。與 sk_buff 類似,net_device 結(jié)構(gòu)比較大,而且包含了很多特性相關(guān)的參數(shù),這些參數(shù)在不同的協(xié)議層中使用。出于這個原因,net_device 結(jié)構(gòu)的組織會有一些改變,用于優(yōu)化協(xié)議棧的性能。 網(wǎng)絡設備可以分為不同的類型,比如以太網(wǎng)卡和令牌環(huán)網(wǎng)卡。net_device 結(jié)構(gòu)中的某些變量對同一類型的設備來說, 取值是相同的; 而某些變量在同一設備的不同工作模式下,取值必須不同。因此,對幾乎所有類型的設備,linux內(nèi)核提供了一個通用的函數(shù)用于初始化那些在所有模式下取值相同的變量。每一個設備驅(qū)動在調(diào)用這個函數(shù)的同時,還初始化那些在當前模式下取值不同的變量。設備驅(qū)動同樣可以覆蓋那些由內(nèi)核初始化的變量(例如,在優(yōu)化設備性能時)。
net_device的定義:
Code
//include/linux/netdevice.h
/*
?*????The?DEVICE?structure.
?*????Actually,?this?whole?structure?is?a?big?mistake.??It?mixes?I/O
?*????data?with?strictly?"high-level"?data,?and?it?has?to?know?about
?*????almost?every?data?structure?used?in?the?INET?module.
?*
?*????FIXME:?cleanup?struct?net_device?such?that?network?protocol?info
?*????moves?out.
?*/
struct?net_device
{
????/*
?????*?This?is?the?first?field?of?the?"visible"?part?of?this?structure
?????*?(i.e.?as?seen?by?users?in?the?"Space.c"?file).??It?is?the?name
?????*?the?interface.
?????*/
????char????????????name[IFNAMSIZ];//網(wǎng)絡設備名稱
????/*
?????*????I/O?specific?fields
?????*????FIXME:?Merge?these?and?struct?ifmap?into?one
?????*/
????unsigned?long????????mem_end;????/*?shared?mem?end????*/
????unsigned?long????????mem_start;????/*?shared?mem?start????*/
????unsigned?long????????base_addr;????/*?device?I/O?address????*/
????unsigned?int????????irq;????????/*?device?IRQ?number????*/
????/*
?????*????Some?hardware?also?needs?these?fields,?but?they?are?not
?????*????part?of?the?usual?set?specified?in?Space.c.
?????*/
????unsigned?char????????if_port;????/*?Selectable?AUI,?TP,..*/
????unsigned?char????????dma;????????/*?DMA?channel????????*/
????unsigned?long????????state;
????struct?net_device????*next;
????
????/*?The?device?initialization?function.?Called?only?once.?*/
????int????????????(*init)(struct?net_device?*dev);
????/*?-------?Fields?preinitialized?in?Space.c?finish?here?-------?*/
????struct?net_device????*next_sched;
????/*?Interface?index.?Unique?device?identifier????*/
????int????????????ifindex;
????int????????????iflink;
????struct?net_device_stats*?(*get_stats)(struct?net_device?*dev);
????struct?iw_statistics*????(*get_wireless_stats)(struct?net_device?*dev);
????/*?List?of?functions?to?handle?Wireless?Extensions?(instead?of?ioctl).
?????*?See?<net/iw_handler.h>?for?details.?Jean?II?*/
????const?struct?iw_handler_def?*????wireless_handlers;
????/*?Instance?data?managed?by?the?core?of?Wireless?Extensions.?*/
????struct?iw_public_data?*????wireless_data;
????struct?ethtool_ops?*ethtool_ops;
????/*
?????*?This?marks?the?end?of?the?"visible"?part?of?the?structure.?All
?????*?fields?hereafter?are?internal?to?the?system,?and?may?change?at
?????*?will?(read:?may?be?cleaned?up?at?will).
?????*/
????/*?These?may?be?needed?for?future?network-power-down?code.?*/
????unsigned?long????????trans_start;????/*?Time?(in?jiffies)?of?last?Tx????*/
????unsigned?long????????last_rx;????/*?Time?of?last?Rx????*/
????unsigned?short????????flags;????/*?interface?flags?(a?la?BSD)????*/
????unsigned?short????????gflags;
????????unsigned?short??????????priv_flags;?/*?Like?'flags'?but?invisible?to?userspace.?*/
????????unsigned?short??????????unused_alignment_fixer;?/*?Because?we?need?priv_flags,
?????????????????????????????????????????????????????????*?and?we?want?to?be?32-bit?aligned.
?????????????????????????????????????????????????????????*/
????unsigned????????mtu;????/*?interface?MTU?value????????*/
????unsigned?short????????type;????/*?interface?hardware?type????*/
????unsigned?short????????hard_header_len;????/*?hardware?hdr?length????*/
????void????????????*priv;????/*?pointer?to?private?data????*/
????struct?net_device????*master;?/*?Pointer?to?master?device?of?a?group,
??????????????????????*?which?this?device?is?member?of.
??????????????????????*/
????/*?Interface?address?info.?*/
????unsigned?char????????broadcast[MAX_ADDR_LEN];????/*?hw?bcast?add????*/
????unsigned?char????????dev_addr[MAX_ADDR_LEN];????/*?hw?address????*/
????unsigned?char????????addr_len;????/*?hardware?address?length????*/
????unsigned?short??????????dev_id;????????/*?for?shared?network?cards?*/
????struct?dev_mc_list????*mc_list;????/*?Multicast?mac?addresses????*/
????int????????????mc_count;????/*?Number?of?installed?mcasts????*/
????int????????????promiscuity;
????int????????????allmulti;
????int????????????watchdog_timeo;
????struct?timer_list????watchdog_timer;
????/*?Protocol?specific?pointers?*/
????
????void?????????????*atalk_ptr;????/*?AppleTalk?link?????*/
????void????????????*ip_ptr;????/*?IPv4?specific?data????*/??
????void????????????????????*dn_ptr;????????/*?DECnet?specific?data?*/
????void????????????????????*ip6_ptr;???????/*?IPv6?specific?data?*/
????void????????????*ec_ptr;????/*?Econet?specific?data????*/
????void????????????*ax25_ptr;????/*?AX.25?specific?data?*/
????struct?list_head????poll_list;????/*?Link?to?poll?list????*/
????int????????????quota;
????int????????????weight;
????struct?Qdisc????????*qdisc;
????struct?Qdisc????????*qdisc_sleeping;
????struct?Qdisc????????*qdisc_ingress;
????struct?list_head????qdisc_list;
????unsigned?long????????tx_queue_len;????/*?Max?frames?per?queue?allowed?*/
????/*?ingress?path?synchronizer?*/
????spinlock_t????????ingress_lock;
????/*?hard_start_xmit?synchronizer?*/
????spinlock_t????????xmit_lock;
????/*?cpu?id?of?processor?entered?to?hard_start_xmit?or?-1,
???????if?nobody?entered?there.
?????*/
????int????????????xmit_lock_owner;
????/*?device?queue?lock?*/
????spinlock_t????????queue_lock;
????/*?Number?of?references?to?this?device?*/
????atomic_t????????refcnt;
????/*?delayed?register/unregister?*/
????struct?list_head????todo_list;
????/*?device?name?hash?chain?*/
????struct?hlist_node????name_hlist;
????/*?device?index?hash?chain?*/
????struct?hlist_node????index_hlist;
????/*?register/unregister?state?machine?*/
????enum?{?NETREG_UNINITIALIZED=0,
???????????NETREG_REGISTERING,????/*?called?register_netdevice?*/
???????????NETREG_REGISTERED,????/*?completed?register?todo?*/
???????????NETREG_UNREGISTERING,????/*?called?unregister_netdevice?*/
???????????NETREG_UNREGISTERED,????/*?completed?unregister?todo?*/
???????????NETREG_RELEASED,????????/*?called?free_netdev?*/
????}?reg_state;
????/*?Net?device?features?*/
????int????????????features;
#define?NETIF_F_SG????????1????/*?Scatter/gather?IO.?*/
#define?NETIF_F_IP_CSUM????????2????/*?Can?checksum?only?TCP/UDP?over?IPv4.?*/
#define?NETIF_F_NO_CSUM????????4????/*?Does?not?require?checksum.?F.e.?loopack.?*/
#define?NETIF_F_HW_CSUM????????8????/*?Can?checksum?all?the?packets.?*/
#define?NETIF_F_HIGHDMA????????32????/*?Can?DMA?to?high?memory.?*/
#define?NETIF_F_FRAGLIST????64????/*?Scatter/gather?IO.?*/
#define?NETIF_F_HW_VLAN_TX????128????/*?Transmit?VLAN?hw?acceleration?*/
#define?NETIF_F_HW_VLAN_RX????256????/*?Receive?VLAN?hw?acceleration?*/
#define?NETIF_F_HW_VLAN_FILTER????512????/*?Receive?filtering?on?VLAN?*/
#define?NETIF_F_VLAN_CHALLENGED????1024????/*?Device?cannot?handle?VLAN?packets?*/
#define?NETIF_F_TSO????????2048????/*?Can?offload?TCP/IP?segmentation?*/
#define?NETIF_F_LLTX????????4096????/*?LockLess?TX?*/
????/*?Called?after?device?is?detached?from?network.?*/
????void????????????(*uninit)(struct?net_device?*dev);
????/*?Called?after?last?user?reference?disappears.?*/
????void????????????(*destructor)(struct?net_device?*dev);
????/*?Pointers?to?interface?service?routines.????*/
????int????????????(*open)(struct?net_device?*dev);
????int????????????(*stop)(struct?net_device?*dev);
????int????????????(*hard_start_xmit)?(struct?sk_buff?*skb,
????????????????????????????struct?net_device?*dev);
#define?HAVE_NETDEV_POLL
????int????????????(*poll)?(struct?net_device?*dev,?int?*quota);
????int????????????(*hard_header)?(struct?sk_buff?*skb,
????????????????????????struct?net_device?*dev,
????????????????????????unsigned?short?type,
????????????????????????void?*daddr,
????????????????????????void?*saddr,
????????????????????????unsigned?len);
????int????????????(*rebuild_header)(struct?sk_buff?*skb);
#define?HAVE_MULTICAST?????????????
????void????????????(*set_multicast_list)(struct?net_device?*dev);
#define?HAVE_SET_MAC_ADDR???????????
????int????????????(*set_mac_address)(struct?net_device?*dev,
???????????????????????????void?*addr);
#define?HAVE_PRIVATE_IOCTL
????int????????????(*do_ioctl)(struct?net_device?*dev,
????????????????????????struct?ifreq?*ifr,?int?cmd);
#define?HAVE_SET_CONFIG
????int????????????(*set_config)(struct?net_device?*dev,
??????????????????????????struct?ifmap?*map);
#define?HAVE_HEADER_CACHE
????int????????????(*hard_header_cache)(struct?neighbour?*neigh,
?????????????????????????????struct?hh_cache?*hh);
????void????????????(*header_cache_update)(struct?hh_cache?*hh,
???????????????????????????????struct?net_device?*dev,
???????????????????????????????unsigned?char?*??haddr);
#define?HAVE_CHANGE_MTU
????int????????????(*change_mtu)(struct?net_device?*dev,?int?new_mtu);
#define?HAVE_TX_TIMEOUT
????void????????????(*tx_timeout)?(struct?net_device?*dev);
????void????????????(*vlan_rx_register)(struct?net_device?*dev,
????????????????????????????struct?vlan_group?*grp);
????void????????????(*vlan_rx_add_vid)(struct?net_device?*dev,
???????????????????????????unsigned?short?vid);
????void????????????(*vlan_rx_kill_vid)(struct?net_device?*dev,
????????????????????????????unsigned?short?vid);
????int????????????(*hard_header_parse)(struct?sk_buff?*skb,
?????????????????????????????unsigned?char?*haddr);
????int????????????(*neigh_setup)(struct?net_device?*dev,?struct?neigh_parms?*);
????int????????????(*accept_fastpath)(struct?net_device?*,?struct?dst_entry*);
#ifdef?CONFIG_NETPOLL
????int????????????netpoll_rx;
#endif
#ifdef?CONFIG_NET_POLL_CONTROLLER
????void????????????????????(*poll_controller)(struct?net_device?*dev);
#endif
????/*?bridge?stuff?*/
????struct?net_bridge_port????*br_port;
#ifdef?CONFIG_NET_DIVERT
????/*?this?will?get?initialized?at?each?interface?type?init?routine?*/
????struct?divert_blk????*divert;
#endif?/*?CONFIG_NET_DIVERT?*/
????/*?class/net/name?entry?*/
????struct?class_device????class_dev;
????/*?how?much?padding?had?been?added?by?alloc_netdev()?*/
????int?padded;
};
??? net_device結(jié)構(gòu)主要分為以下幾部分:
1.1、?? ?通用字段
name:
網(wǎng)絡適配器的名稱,比如eth0。在注冊網(wǎng)絡設備時可以為設備分配一個名稱,便必須唯一。
next:
所有的網(wǎng)絡設備組成一個由dev_base開頭的鏈表。
int ifindex :
全局唯一的設備ID。在每個設備注冊時,調(diào)用dev_new_index 生成。
int iflink:
這個變量主要被(虛擬)隧道設備使用,用于標識隧道的真實設備。
state:
它包含一組被網(wǎng)絡隊列子系統(tǒng)使用的標記。這些標記的值是枚舉類型netdev_state_t中的索引值,這個類型的定義在 include/linux/netdevice.h 中,每個標記都是諸如__LINK_STATE_XOFF 這樣的常量。每一位都可以通過函數(shù) set_bit 和 clear_bit 設置或清除,但通常情況下,都會有一個包裝函數(shù)來隱藏標記位的信息。例如,在網(wǎng)絡隊列子系統(tǒng)停止一個設備隊列時,它調(diào)用函數(shù) netif_stop_queue,這個函數(shù)的定義如下:
? static inline void netif_stop_queue(struct net_device *dev)
{
??????? ...
??????? set_bit(_ _LINK_STATE_XOFF, &dev->state);
}
trans_start:
最后一個幀開始發(fā)送的時間(用jiffies度量)。設備驅(qū)動在發(fā)送之前設置這個變量。這個變量用來檢測網(wǎng)卡是否在給定的時間內(nèi)把幀發(fā)送了出去。 太長的發(fā)送時間意味
著有錯誤發(fā)生,在這種情況下,設備驅(qū)動會重置網(wǎng)卡。
last_rx :
接收到最后一個包的時間(用jiffies度量)。
xmit_lock 和xmit_lock_owner :
xmit_lock 用來序列化對設備驅(qū)動函數(shù)hard_start_xmit的調(diào)用。這意味著,每個cpu每次只能調(diào)用設備完成一次發(fā)送。xmit_lock_owner 是擁有鎖的 CPU 的 ID。在單cpu 系統(tǒng)上,這個值是 0;在多 cpu 系統(tǒng)中,如果鎖沒有被占用,這個值是-1。內(nèi)核同樣允許不加鎖的發(fā)送,前提條件是設備驅(qū)動必須支持這個功能。
struct hlist_node name_hlist
struct hlist_node index_hlist
? 把net_device結(jié)構(gòu)鏈接到兩個哈希表中。
1.2、?? ?硬件相關(guān)
unsigned int irq
? 設備中斷號。它可以被多個設備共享。設備驅(qū)動調(diào)用request_irq來分配這個值,并
調(diào)用free_irq來釋放它。
unsigned char if_port
? 接口的端口類型。有些設備可以支持多種接口(最常見的組合是 BNC+RJ45),用戶可以根據(jù)需要來選擇使用哪種接口。這個變量用來設置設備的接口類型。如果配置命令沒有指定設備的接口類型,設備驅(qū)動就使用缺省的類型。在某些情況下,一個設備驅(qū)動可以處理多種接口類型;在這種情況下,設備驅(qū)動可以按一定的順序來測試每個接口的類型。下面的代碼片斷展示了一個設備驅(qū)動如何根據(jù)配置來設置接口的類型:
? 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 通道,內(nèi)核在 kernel/dma.c 中定義了兩個函數(shù)request_dma和free_dma。為了在獲取dma通道后,啟用或者停止dma通道,內(nèi)核定義了兩個函數(shù)enable_dma和disable_dma。這兩個函數(shù)的實現(xiàn)與
體系結(jié)構(gòu)相關(guān),所以在 include/asm-architecture 下有相關(guān)的文件(例如include/asm-i386)。這些函數(shù)被 ISA 設備使用;PCI 設備不使用這些函數(shù),它們使
用其他函數(shù)。并不是所有的設備都可以使用dma,因為有些總線不支持dma。
?unsigned long mem_start
unsigned long mem_end
? 這兩個變量描述設備與內(nèi)核通信所用到的內(nèi)存邊界。它們由設備驅(qū)動初始化,并且只能被設備驅(qū)動訪問;高層協(xié)議不需要關(guān)心這塊內(nèi)存。
unsigned long base_addr
? 映射到設備內(nèi)存空間中I/O 內(nèi)存起始地址。
1.3、?? ?物理層相關(guān)
unsigned mtu
? MTU 的意思是最大傳輸單元,它表示設備可以處理幀的最大長度。不同設備的MTU值:
unsigned short type
??? 設備類型(以太網(wǎng),幀中繼等)。在include/linux/if_arp.h 中有完整的類型列表。
unsigned short hard_header_len
? 以字節(jié)為單位的幀頭部長度。例如,以太網(wǎng)幀的頭是 14 字節(jié)。某種設備所支持幀的頭部長度在相應的設備頭文件中定義。對以太網(wǎng)來說,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,以字節(jié)為單位。addr_len 的大小與設備類型有關(guān)。以太網(wǎng)地址的長度是8。
int promiscuity
promiscuity計數(shù)器來標識設備是否工作在混雜模式。之所以使用計數(shù)器而不是一個標志位的原因是:可能有多個用戶程序設置設備工作在混雜模式下。因此,每次進入混雜模式,計數(shù)器加一;退出混雜模式,計數(shù)器減一。只有計數(shù)器為0 時,設備才退出混雜模式。這個變量通常調(diào)用函數(shù)dev_set_promiscuity 來設置。
struct dev_mc_list *mc_list
? 指向dev_mc_list結(jié)構(gòu)
int mc_count
? 設備多播地址的數(shù)量,它同樣表示mc_list所指向鏈表的長度。
int allmulti
? 如果是非零值,那么設備將監(jiān)聽所有的多播地址。和 promiscuity 一樣,這個變量是一個計數(shù)器而不僅僅是一個布爾值。這是因為多個設備(比如VLAN和bonding
設備)可能獨立地要求監(jiān)聽所有地址。如果這個變量的值從0變?yōu)榉橇?#xff0c;內(nèi)核會調(diào)用函數(shù)dev_set_allmulti通知設備監(jiān)聽所有的多播地址。如果這個值變?yōu)?,則停止監(jiān)聽所有的多播地址。
1.4、?? ?協(xié)議相關(guān)
void *atalk_ptr
void *ip_ptr
void *dn_ptr
void *ip6_ptr
void *ec_ptr
void *ax25_ptr
這六個變量指向特定協(xié)議的數(shù)據(jù)結(jié)構(gòu),每個數(shù)據(jù)結(jié)構(gòu)都包含協(xié)議私有的參數(shù)。例如,ip_ptr 指向一個 in_device 類型的結(jié)構(gòu)(盡管 ip_ptr 的類型是 void*),它包含 IPv4相關(guān)的參數(shù),其中包括設備的 IP 地址列表等。
1.5、?? ?流量管理
Linux 流量控制子系統(tǒng)的功能已經(jīng)非常強大,并且已經(jīng)成為 Linux 內(nèi)核中的一個重要組件。相關(guān)的內(nèi)核選項是 “Device drivers ->Networking support ->Networking options ->QoS and/or fair queueing”。net_device中的相關(guān)變量包括:
struct net_device *next_sched
? 被內(nèi)核軟中斷使用。
struct Qdisc *qdisc
struct Qdisc *qdisc_sleeping
struct Qdisc *qdisc_ingress
struct list_head qdisc_list
? 這些變量管理設備的接收,發(fā)送隊列,并且可以被不同的cpu訪問。
spinlock_t queue_lock
spinlock_t ingress_lock
? 流量控制子系統(tǒng)為每個網(wǎng)絡設備定義了一個私有的發(fā)送隊列。 queue_lock用于避免并發(fā)的訪問(參見第11章)。ingress_lock 用于保護接收隊列。
unsigned long tx_queue_len
? 設備發(fā)送隊列的長度。如果內(nèi)核中包含了流量控制子系統(tǒng),這個變量可能沒有什么用(只有幾個排隊策略會使用它)。常見設備的 tx_queue_len 值(這個值可以通過sysfs文件系統(tǒng)修改(在/sys/class/net/device_name/目錄下)):
1.6、?? ?設備驅(qū)動程序相關(guān)
int (*init)(...)
void (*uninit)(...)
void (*destructor)(...)
int (*open)(...)
int (*stop)(...)
用于初始化,清除,銷毀,啟用和停止一個設備。這些函數(shù)并不是每個設備都會用到。
int (*hard_start_xmit)(...)
?發(fā)送一個幀。
int (*hard_header)(...)
?根據(jù)源和目標的第2層地址創(chuàng)建第2層報文頭。
int (*rebuild_header)(...)
?負責在傳送包之前重建第2導報文頭。
int (*set_mac_address)(...)
? 修改設備的 MAC 地址。如果設備不提供這個功能(比如網(wǎng)橋設備),可以把這個指針設置為NULL。
int (*change_mtu)(...)
? 修改設備的MTU,修改mtu 不會對設備驅(qū)動有任何影響,它只是讓協(xié)議棧軟件可以根據(jù)新的mtu 正確地處理分片。 新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎!定制產(chǎn)品紅包拿不停!
總結(jié)
以上是生活随笔為你收集整理的Linux网络协议栈(三)——网络设备(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 艾伟_转载:C# WinForm开发系列
- 下一篇: 4.3