Linux網絡子系統的頂部是系統調用接口層。它為用戶空間提供的應用程序提供了一種訪問內核網絡子系統的方法(socket)。位于其下面是一個協議無關層,它提供一種通用的方法來使用傳輸層協議。然后是具體協議的實現,在Linux中包括內核的協議TCP,UDP,當然還有IP。然后是設備無關層,它提供了協議與設備驅動通信的通用接口,最下面是設備的驅動程序。
設備無關接口將協議與各種網絡驅動連接在一起,這一層提供一組通用函數供底層網絡設備驅動使用,讓它們可以對高層協議棧進行操作。需要從協議層向設備發生數據,需要調用dev_queue_xmit函數,這個函數對數據進行列隊,然后交由底層驅動程序的hard_start_xmit方法最終完成傳輸。接收通常是使用netif_rx執行的。當底層設備程序接收到一個報文(發生中斷)時,就會調用netif_rx將數據上傳至設備無關層。
下圖為設備無關層到驅動層的體系結構
2.網絡設備描述(structnet_device)
每一個網絡設備都由struct net_device來描述,該結構可使用如下內核函數進行動態分配
struct net_device *alloc_net(intsizeof_priv, const char *mask, void(*setup)(struct net_deive *))
sizeof_priv是私有數據區大小;mask是設備名,setup是初始化函數,在注冊該設備時,該函數被調用。也就是net_deivce的init成員。
struct net_device *alloc_etherdev(intsizeof_priv)
這個函數和上面的函數不同之處在于內核知道會將該設備做一個以太網設備看待并做一些相關的初始化。
net_device結構可分為全局成員、硬件相關成員、接口相關成員、設備方法成員和公用成員等五個部分
主要全局成員
char name[INFAMSIZ]
設備名,如:eh%d
unsigned long state
設備狀態
unsigned long base_addr
I/O基地址
unsigned int irq
中斷號
主要設備方法有
int (*init)(struct net_device *dev)
初始化函數,該函數在register_netdev時被調用來完成對net_device結構的初始化
int (*open)(struct net_device *dev)
打開接口。ifconfig激活時,接口將被打開
int (*stop)(struct net_deivce *dev)
停止接口,ifconfig eth% down時調用
int (*hard_start_xmit)(struct sk_buf*skb,struct net_device *dev)
數據發送函數
int (*do_ioctl)(struct net_deive *dev,struct ifreq *ifr, int cmd)
處理特定于接口的ioctl命令(sock_ioctl)進行調用。
int (*set_mac_address)(struct net_device*dev, void *addr)
改變MAC地址的函數,需要硬件支持該功能。
網絡設備的注冊
網絡設備注冊方式與字符驅動不同之處在于它沒有主次設備號,并使用下面的函數注冊
int register_netdev(struct net_deivce*dev)
網絡設備的注銷
void unregister_netdev(struct net_device*dev)
3.網絡數據包描述(sk_buff)
Linux內核中每個網絡數據包都由一個套接字緩沖區結構structsk_buff描述,既每個sk_buff結構就是一個包,指向sk_buff的指針通常被稱作skb
sk_buff中重要的數據成員
struct device *dev;處理該包得設備
__u32 sadd;r//IP元地址
__u32 daddr;//IP目的地址
__u32 raddr;//IP路由器地址
unsigned char *head;//分配空間的開始
unsigned char *data;//有效數據的開始
unsigned char *tail;//有效數據的結束
unsigned char *end;//分配空間的結束
unsigned long len;//有效數據的長度
sk_buff操作
struct sk_buff *alloc_skb(unsigned intlen, int priority)
分配一個sk_buff結構,供協議棧代碼使用
struct sk_buff *dev_alloc_skb(unsignedint len)
分配一個sk_buff結構。供驅動代碼使用
void kfree_skb(struct sk_buff *skb)
void dev_kfree_skb(struct sk_buff *skb)
釋放sk_buff結構
unsigned char *skb_push(struct sk_buff*skb,int len)
將data指針向前移動len長度。并返回移動之后的值。用于向skb有效數據區域前端添加數據(包頭)。
unsigned char *skb_put(struct sk_buff*skb, int len)
將taill指針向后移動len長度,并返回tail移動之前的值。用于向skb有效數據區域末尾添加數據。
4.驅動的實現
1).初始化(init)
設備探測工作在init方法中進行,一般調用一個稱之為probe方法的函數
初始化的主要工作時檢測設備,配置和初始化硬件,最后向系統申請這些資源。此外填充該設備的dev結構,我們調用內核提供的ether_setup方法來設置一些以太網默認的設置。
2)打開(open)
open這個方法在網絡設備驅動程序里是網絡設備被激活時被調用(即設備狀態由down變成up)
實際上很多在初始化的工作可以放到這里來做。比如說資源的申請,硬件的激活。如果dev->open返回非0,則硬件狀態還是down
3)關閉(stop)
stop方法做和open相反的工作
可以釋放某些資源以減少系統負擔
stop是在設備狀態由up轉為down時被調用
4)發送(hard_start_xmit)
在系統調用的驅動程序的hard_start_xmit時,發送的數據放在一個sk_buff結構中。一般的驅動程序傳給硬件發出去。也有一些特殊的設備比如說loopback把數據組成一個接收數據在傳送給系統或者dummy設備直接丟棄數據。
如果發送成功,hard_start_xmit方法釋放sk_buff。如果設備暫時無法處理,比如硬件忙,則返回1。
5)接收
驅動程序并存在一個接受方法。當有數據收到時驅動程序調用netif_rx函數將skb交交給設備無關層。
一般設備收到數據后都會產生一個中斷,在中斷處理程序中驅動程序申請一塊sk_buff(skb)從硬件中讀取數據位置到申請號的緩沖區里。
接下來填充sk_buff中的一些信息。
中斷有可能是收到數據產生也可能是發送完成產生,中斷處理程序要對中斷類型進行判斷,如果是收到數據中斷則開始接收數據,如果是發送完成中斷,則處理發送完成后的一些操作,比如說重啟發送隊列。
?
評論
查看更多