1. 程式人生 > >Linux核心程式碼筆記6----網路模型

Linux核心程式碼筆記6----網路模型

網路協議 TCP/IP模型
  • 應用層 (TELNET、FTP、DNS)
  • 傳輸層(TCP、UDP)
  • Internet(IP)
  • 網路介面物理層(PPP/SLIP、LAN)
Internet協議     IP定義了一個協議,而不是一個連線。IP主要負責資料報在計算機之間的定址問題,並管理這些資料報的分段過程。傳輸具有“不可靠性”,驗證和流量控制交給其他層完成。IP是無連線的。     面向連線:通訊雙方在通訊時,事先建立一條通訊線路。過程有建立連線、使用連線、釋放連線等;     無連線:   通訊雙方事先不需要建立通訊線路,而是把帶有目的的地址的包送到線路上,由系統選定路線。 TCP協議     TCP可以向上層提供面向連線的協議,使上層啟動應用程式。作用是提供可靠通訊的有效報文協議。 套接字
    Linux支援多種套接字種類,不同的套接字種類稱為“地址族”。Linux將上述套接字地址族抽象為統一的BSD套接字介面,比如stream、datagram、raw等。 網路裝置介面     網路裝置結構:
    網路裝置驅動程式功能:
  • 傳送資料
        驅動將來自協議層的網路緩衝區通過hard_start_xmit()傳送到物理介質,並接收硬體產生的應答訊號。         dev->hard_start_xmit():將網路緩衝區,也就是sk_buff傳送到硬體裝置。如果裝置不能接受緩衝區,它就會返回1。如果協議層決定釋放被裝置拋棄的緩衝區,那麼緩衝區就不會再被送回裝置;如果裝置知道緩衝區短時間內不被能傳送,例如裝置嚴重堵塞,那麼它就呼叫  dev_kfree_skb
()函式丟掉緩衝區,該函式返回零值標明緩衝區已經被處理完畢。

        當緩衝區被傳送到硬體以後,硬體應答訊號標識傳輸已經完畢,驅動程式必須呼叫dev_kfree_skb(skb, FREE_WRITE)函式釋放緩衝區,一旦該呼叫結束,緩衝區就會很自然地消失,這樣,驅動程式就不能再涉及緩衝區了。sk_buff是被鎖住的(locked),確保其他程式不會存取它。

        資料幀在傳送之前先要排成對列,在加入佇列之前,還要在每個資料幀的開始新增硬體幀頭,這項工作對於資料傳送非常必要。網路裝置驅動程式提供了一個dev->hard_header()例程,來完成新增硬體幀頭的工作。協議層在傳送資料之前會在緩衝區的開始留下至少 dev->hard_header_len 

長度位元組的空閒空間。這樣dev->hard_header()程式只要呼叫 skb_push(),然後正確填入硬體幀頭就可以了。

  • 接收資料
        驅動接收來自網路上的資料幀,將其轉換為能被網路協議識別的網路緩衝區,然後傳遞給netif_rx()函式,將資料幀傳遞到網路協議層做進一步處理。         網路裝置資料結構net_device,是網路驅動最重要的部分,位置在include/linux/netdevice.h。所有的網路裝置的資訊和操作都儲存在裝置資料結構中,每註冊一個網路裝置,都需要提供資料結構中各個域的資料。dev_queue_xmit()和netif_rx()使用了sk_buff結構(意為套接字緩衝區),定義在include/linux/skbuff.h中,用於Linux網路子系統各層間傳輸資料,是一個雙向連結串列。         網路裝置驅動程式沒有關於接收的處理。當它收到資料後都會產生一箇中斷,中斷處理程式呼叫 dev_alloc_skb(),申請一個大小合適的緩衝區(sk_buff),把從硬體傳來的資料放入緩衝區。接著,裝置驅動程式分析資料包的型別,把 skb->dev設定為接收資料的裝置型別 ,把 skb->protocol設定為資料幀描述的協議型別,這樣,資料幀就可以被髮送到正確的協議層。硬體幀頭指標儲存在 skb->mac.raw中,並且硬體幀頭通過呼叫skb_pull()被去掉,因此網路協議就不涉及硬體的資訊。最後還要設定skb->pkt_type,標明鏈路層資料型別,裝置驅動程式必須按以下型別設定skb->pkt_type

PACKET_BROADCAST     連結層廣播地址  

PACKET_MULTICAST      連結層多路地址   

PACKET_SELF   發給自己的資料幀   

PACKET_OTHERHOST    發向另一個主機的資料幀 (監聽模式時會有到) 

        最後,裝置驅動程式呼叫netif_rx()把緩衝區向上傳遞給協議層。netif_rx()將資料放入處理佇列後返回,真正的處理是在中斷返回後,可以減少中斷時間。一旦netif_rx( )被呼叫,緩衝區就不在屬裝置驅動程式所有,它不能被修改,而且裝置驅動程式也不能再涉及它了。

        在協議層,接收資料包的流程控制分兩個層次: 首先,netif_rx()函式限制了從物理層到協議層的資料幀的數量。第二,每一個套接字都有一個佇列,限制從協議層到套接字層的資料幀的數量。在傳輸方面,驅動程式的dev->tx_queue_len 引數用來限制佇列的長度。佇列的長度通常是100幀,在進行大量資料傳輸的高速連線中,它足以容納下所有等待傳輸的緩衝區,不會出現大量緩衝區阻塞的情況。在低速連線中,例如Slip 連線,佇列的長度長設為10幀左右,因為傳輸10幀的資料就要花費數秒的時間排列資料。

相關函式     初始化
int (*init)(struct net_device *dev);
        裝置初始化和註冊時呼叫,執行的是底層的確認和檢查工作,對硬體資源的配置。     開啟裝置
int (*open)(struct net_device *dev);
        網路裝置啟用的時候呼叫(裝置狀態down->up)。主要功能:資源申請、硬體啟用等。若驅動作為模組裝入,open()裡呼叫MOD_INC_USE_COUNT,將引用計數器加1。         在使用者態執行ifconfig eth0 up命令時,要執行兩個任務。首先,它通過ioctl(SIOCSIFADDR)(Socket I/O Control Set Interface Address)賦予地址,然後通過ioctl(SIOCSIFFLAGS)(Socket I/O Control Set Interface Flags)設定dev->flag中的IFF_UP標誌以開啟介面。這個呼叫會使得裝置的open方法得到呼叫。類似的,在介面關閉時,ifconfig使用ioctl(SIOCSIFFLAGS)來清理IFF_UP標誌,然後呼叫stop函式。     關閉裝置
int (*stop)(struct net_device *dev);
        關閉時呼叫(裝置狀態up->down)。若驅動作為模組裝入,stop()中需呼叫MOD_DEC_USE_COUNT。     分配緩衝區

struct sk_buff *alloc_skb(unsigned int len, int priority);   

struct sk_buff *dev_alloc_skb(unsigned int len);  

        分配一個緩衝區。alloc_skb函式分配一個緩衝區並初始化skb->data和skb->tail為skb->head。引數len為資料緩衝區的空間大小,通常以L1_CACHE_BYTES位元組(對ARM為32)對齊,引數priority為記憶體分配的優先順序。dev_alloc_skb()函式以GFP_ATOMIC優先順序進行skb的分配。

    釋放緩衝區

void kfree_skb(struct sk_buff *skb);    void dev_kfree_skb(struct sk_buff *skb);
        sk_buff結構中幾個資料指標指向如圖:
        對sk_buff操作的幾個函式:
unsigned char *skb_put(struct sk_buff *skb, int len);    unsigned char *skb_push(struct sk_buff *skb, int len);    unsigned char *skb_pull(struct sk_buff *skb, int len);    void skb_reserve(struct sk_buff ×skb, int len);  
操作函式
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); 
        乙太網的mac地址是固定的,為了高效,第一個包去詢問mac地址,得到對應的mac地址後就會作為cache把mac地址儲存起來。以後每次發包不用詢問了,直接把包的地址拷貝出來。
void (*tx_timeout)(struct net_device *dev);  
        資料包傳送超時失敗時呼叫。
struct net_device_stats *(*get_stats)(struct net_device *dev);
        統計裝置資訊。
int (*do_ioctl)(struct net_device *dev, struct ifmap *map);
        事先自定義的ioctl命令。 reference 1、深入分析Linux核心原始碼 2、Linux驅動程式開發例項 3、Linux驅動修煉之道