?
????? 圖 6
?
?????? 由圖6我們看到,ednet_rx()并不是網絡設備的一個操作,而是模塊中的一個函數。在實際的網卡驅動程序中,當網卡確實接收到數據的時候,由網絡中斷喚醒等待接收數據的用戶進程,也就是說,ednet_rx()應該由那個網絡中斷處理例程調用。我們這里并沒有中斷,所以字符設備的device_write()可以看成是一個"中斷例程",也就是說,用戶空間往字符寫操作的時候,也就調用了網絡設備的數據接收內核例程ednet_rx()了。然后ednet_rx()會把原始的數據包發送到TCP/IP上層進行處理,這一切均依賴于內核API 函數netif_rx()。ednet_rx()就需要sk_buff數據結構(
?????? 下面介紹一下網絡設備的主要操作例程ednet_open()、ednet_release()、ednet_tx()、ednet_stats ()、ednet_change_mtu()、ednet_header()。網絡設備文件操作結構struct net_device(
?????? ednet_open:?? int? (*open)(struct net_device *dev);
????????ednet_release:? int? (*stop)(struct net_device *dev);
????????ednet_tx:???? int? (*hard_start_xmit) (struct sk_buff *skb,struct net_device *dev);
?????? ednet_stats:?? struct net_device_stats* (*get_stats)(struct net_device *dev);
?????? ednet_change_mtu:int?(*change_mtu)(struct net_device *dev, int new_mtu);
??????? ednet_header:? int? (*hard_header) (struct sk_buff *skb,
??????struct net_device *dev,
??????unsigned short type,
??????void *daddr,
??????void *saddr,
??????unsigned len);
??????? 操作int ednet_open(struct net_device *dev)的作用是打開偽網絡接口設備,獲得其需要的I/O端口、IRQ等,但是本網絡接口不需要和實際硬件打交道,所以不需要自動獲得或者賦予I/O端口值,也不需要IRQ中斷號,唯一需要程序指定的是其偽硬件地址(這個硬件地址是"0ED000",ifconfig可以看到其硬件地址是 00:45:44:30:30:30,struct net_device中的dev_addr域存放網絡接口的物理地址。操作ednet_open()必須調用netif_start_queue()內核API開啟網絡接口接收和發送數據隊列。
?????? 當接口關閉的時候,int ednet_release(struct net_device *dev)例程被系統調用,在ednet_release()中調用netif_stop_queque()將停止接收和發送隊列的工作。
?????? 偽網絡設備驅動的傳送例程int ednet_tx(struct sk_buff *skb, struct net_device *dev)將把要發送的網絡數據包寫入字符設備ed[ED_TX_DEVICE]。在發送完畢數據包的時候,dev_kfree_skb() Kernel API釋放由上層協議棧分配的sk_buff數據塊。偽網絡接口在進行硬件傳輸的時候,需要為網絡數據包打上時間戳。如果傳送數據包的時候超時,將調用超時處理例程ednet_tx_timeout()超時處理例程。例程ednet_tx()調用真正的"硬件"傳送例程ednet_hw_tx()在實際的網卡驅動程序中,就是真正向特定的網絡硬件設備寫數據的程序。我們看到,我們的"硬件"就是本文前面描述的字符設備,字符設備的操作例程.kernel_write()在ednet_hw_tx()將被調用。
?????? 如果我們希望使用ifconfig看到偽網絡接口的統計信息,那么系統就調用 struct net_device_stats *ednet_stats(struct net_device *dev)。我們看到,網絡接口的統計信息被放到設備的私有數據指針指向的內存。網絡數據信息的統計結構被放在內核結構struct net_device_stats中。
?????? 在TCP會話中,也許要協商MTU的大小,int ednet_change_mtu(struct net_device *dev, int new_mtu)可以隨時改變MTU的大小。比如在使用FTP協議的時候,在傳送數據庫的時候,MTU可能被協商為最大,以提高網絡傳送吞吐量。由于改變了MTU,存放網絡數據的字符設備初始化分配的緩存區就要重新被分配,并把已經存放數據的舊的緩存區的內容拷貝到新的緩存區中,所以,當MTU改變大小的時候,那么就要使用kmalloc(new_mtu ,GFP_KERNEL)重新分配緩存區。讀者可以根據自己的需要定義新的緩存區大小。kfree()是內核API,負責釋放內核空間的內存,它的使用方法和用戶空間的free()系統調用一致,這里就不列舉ed_realloc()函數的源程序了。
?????? IP數據包在被網絡接口發送前,需要構建其以太網頭信息int ednet_header(struct sk_buff *skb,struct net_device *dev,unsigned short type,void *daddr,void *saddr,unsigned int len)例程完成此功能,我們看到網絡數據包的以太源、目的地址,都是從發送這個數據包的網絡接口設備數據結構struct net_device中得到的。源地址和目的地址信息是從網絡設備結構得到的。在編譯本程序的時候,如果發現htons()這個函數沒有定義,可以這樣定義htons()為:#define htons(x) ((x>>8) | (x<<8)) 。
???????? 因為偽網絡接口沒有使用ARP獲得硬件地址,所以我們可以把我們自己定義的偽硬件地址復制到數據包的以太網包頭。Linux2.4.x使用設備方法hard_header()代替設備方法rebuild_header()。Linux2.x使用的rebuild_header()例程在本文的附加源程序中,這里不再說明。
??????? 編寫用戶空間串口通信程序
??????? 控制串口的server應用程序完成非常簡單的打包和拆包的工作,它沒有差錯控制,沒有重發機制,在實際應用中,需要加上適當的控制協議。server創建的子進程負責從串口讀取數據并把數據傳送到receiving device /dev/ed_rec;父進程則負責從sending device /dev/ed_tx 讀取需要發送的網絡數據包,然后從串口發送出去。子進程和父進程都是用輪詢方式讀取和寫入設備。Server的程序流圖如圖所示。
???????
?
圖 7
?
?????? 傳送的frame按照SLIP定義的格式:數據的兩頭都是END字符(0300),如圖8所示。
?圖 8
?
?????? 特殊控制字符的定義如下:
?????? #define END????????????? 0300
?????? #define ESC????????????? 0333
?????? #define ESC_END???????? 0334???????????
?????? #define ESC_ESC???????? 0335
???????
評論
查看更多