轉一篇講解網路子系統的文章

來源:互聯網
上載者:User

Linux強大的網路功能是如何?的,讓我們一起進入Linux核心的網路系統瞭解一下吧。

7.1. sk_buff結構
在Linux核心的網路實現中,使用了一個緩衝結構(struct sk_buff)來管理網路報文,這個緩衝區也叫通訊端緩衝。sk_buff是核心網路子系統中最重要的一種資料結構,它貫穿網路報文收發的整個周期。該結構在核心源碼的include/linux/skbuff.h檔案中定義。我們有必要瞭解結構中每個欄位的意義。

一個通訊端緩衝由兩部份組成:

·         報文資料:儲存實際需要通過網路發送和接收的資料。

·         管理資料(struct sk_buff):管理報文所需的資料,在sk_buff結構中有一個head指標指向記憶體中報文資料開始的位置,有一個data指標指向報文資料在記憶體中的具體地址。head和data之間申請有足夠多的空間用來存放報文頭資訊。

struct sk_buff結構在記憶體中的結構:

                 sk_buff
 -----------------------------------   ------------> skb->head
|            headroom               |
|-----------------------------------|  ------------> skb->data
|              DATA                 |               
|                                   |
|                                   |
|                                   |
|                                   |
|                                   |
|                                   |
|                                   |
|                                   |
|                                   |
|-----------------------------------|  ------------> skb->tail
|            tailroom               |
 -----------------------------------   ------------> skb->end
 
7.2. sk_buff結構操作函數
核心通過alloc_skb()和dev_alloc_skb()為通訊端緩衝申請記憶體空間。這兩個函數的定義位於net/core/skbuff.c檔案內。通過這alloc_skb()申請的記憶體空間有兩個,一個是存放實際報文資料的記憶體空間,通過kmalloc()函數申請;一個是sk_buff資料結構的記憶體空間,通過 kmem_cache_alloc()函數申請。dev_alloc_skb()的功能與alloc_skb()類似,它只被驅動程式的中斷所調用,與alloc_skb()比較只是申請的記憶體空間長度多了16個位元組。

核心通過kfree_skb()和dev_kfree_skb()釋放為通訊端緩衝申請的記憶體空間。dev_kfree_skb()被驅動程式使用,功能與kfree_skb()一樣。當skb->users為1時kfree_skb()才會執行釋放記憶體空間的動作,否則只會減少skb->users的值。skb->users為1表示已沒有其他使用者使用該緩衝了。

skb_reserve()函數為skb_buff緩衝結構預留足夠的空間來存放各層網路通訊協定的頭資訊。該函數在在skb緩衝申請成功後,載入報文資料前執行。在執行skb_reserve()函數前,skb->head,skb->data和skb->tail指標的位置的一樣的,都位於skb記憶體空間的開始位置。這部份空間叫做headroom。有效資料後的空間叫tailroom。skb_reserve的操作只是把skb->data和skb->tail指標向後移,但緩衝總長不變。

運行skb_reserve()前sk_buff的結構
 
        sk_buff
 ----------------------   ---------->  skb->head,skb->data,skb->tail
|                      |
|                      |
|                      |
|                      |
|                      |
|                      |
|                      |
|                      |
|                      |
 ---------------------    ---------->  skb->end
 
運行skb_reserve()後sk_buff的結構
 
        sk_buff
 ----------------------   ---------->  skb->head
|                      |
|      headroom        |
|                      |
|--------------------- |  ---------->  skb->data,skb->tail
|                      |
|                      |
|                      |
|                      |
|                      |
 ---------------------    ---------->  skb->end
        
skb_put()向後擴大資料區空間,tailroom空間減少,skb->data指標不變,skb->tail指標下移。

skb_push()向前擴大資料區空間,headroom空間減少,skb->tail指標不變,skb->data指標上移

skb_pull()縮小資料區空間,headroom空間增大,skb->data指標下移,skb->tail指標不變。

skb_shared_info結構位於skb->end後,用skb_shinfo函數申請記憶體空間。該結構主要用以描述data記憶體空間的資訊。

 ---------------------  ----------->  skb->head
|                     |
|                     |
|      sk_buff        |
|                     |
|                     |
|                     |
|---------------------| ----------->  skb->end
|                     |
|   skb_share_info    |
|                     |
 ---------------------
skb_clone和skb_copy可拷貝一個sk_buff結構,skb_clone方式是clone,只產生新的sk_buff記憶體區,不會產生新的data記憶體區,新sk_buff的skb->data指向舊data記憶體區。skb_copy方式是完全拷貝,產生新的sk_buff記憶體區和data記憶體區。。

7.3. net_device結構
net_device結構是Linux核心中所有網路裝置的基礎資料結構。包含網路介面卡的硬體資訊(中斷、連接埠、驅動程式函數等)和高層網路通訊協定的網路設定資訊(IP地址、子網路遮罩等)。該結構的定義位於include/linux/netdevice.h

每個net_device結構表示一個網路裝置,如eth0、eth1...。這些網路裝置通過dev_base線性錶鏈接起來。核心變數dev_base表示登入網路裝置列表的進入點,它指向列表的第一個元素(eth0)。然後各元素用next欄位指向下一個元素(eth1)。使用ifconfig -a命令可以查看系統中所有登入的網路裝置。

net_device結構通過alloc_netdev函數分配,alloc_netdev函數位於net/core/dev.c檔案中。該函數需要三個參數。

·         私人資料結構的大小

·         裝置名稱,如eth0,eth1等。

·         配置常式,這些常式會初始化部分net_device欄位。

分配成功則返回指向net_device結構的指標,分配失敗則返回NULL。

7.4. 網路裝置初始化
在使用網路裝置之前,必須對它進行初始化和向核心註冊該裝置。網路裝置的初始化包括以下步驟:

·         硬體初始化:分配IRQ和I/O連接埠等。

·         軟體初始化:分配IP地址等。

·         功能初始化:QoS等

7.5. 網路裝置與核心的溝通方式
網路裝置(網卡)通過輪詢和中斷兩種方式與核心溝通。

·         輪詢(polling),由核心發起,核心周期性地檢查網路裝置是否有資料要處理。

·         中斷(interrupt),由裝置發起,裝置向核心發送一個硬體中斷訊號。

Linux網路系統可以結合輪詢和中斷兩方式以提高網路系統的效能。共小節重點介紹中斷方式。

每個中斷都會調用一個叫中斷處理器的函數。當驅動程式向核心註冊一個網卡時,會請求和分配一個IRQ號。接著為分配的這個IRQ註冊中斷處理器。註冊和釋放中斷處理器的代碼是架構相關的,不同的硬體平台有不同的代碼實現。實現代碼位於kernel/irq/manage.c和arch/XXX/kernel/irq.c源碼檔案中。XXX是不同硬體架構的名稱,如我們所使用得最多的i386架構。下面是註冊和釋放中斷處理器的函數原型。

int request_irq(unsigned int irq, irq_handler_t handler,
                unsigned long irqflags, const char *devname, void *dev_id)
 
void free_irq(unsigned int irq, void *dev_id) 
核心是通過IRQ號來找到對應的中斷處理器並執行它的。為了找到中斷處理器,核心把IRQ號和中斷處理器函數的關聯起來儲存在全域表(global table)中。IRQ號和中斷處理器的關聯性可以是一對一,也可以是一對多。因為IRQ號是可以被多個裝置所共用的。

通過中斷,網卡裝置可以向驅動程式傳送以下資訊:

·         幀的接收,這是最用的中斷類型。

·         傳送失敗通知,如傳送逾時。

·         DMA傳送成功。

·         裝置有足夠的記憶體傳送資料幀。當外出隊列沒有足夠的記憶體空間存放一個最大的幀時(對於乙太網路卡是1535),網卡產生一個中斷要求以後再傳送資料,驅動程式會禁止資料的傳送,。當有效記憶體空間多於裝置需傳送的最大幀(MTU)時,網卡會發送一個中斷通知驅動程式重新啟用資料傳送。這些邏輯處理在網路卡驅動程式中設計。 netif_stop_queue()函數禁止裝置傳送隊列,netif_start_queue()函數重啟裝置的傳送隊列。這些動作一般在驅動程式的xxx_start_xmit()中處理。

系統的中斷資源是有限的,不可能為每種裝置提供獨立的中斷號,多種裝置要共用有限的中斷號。上面我們提到中斷號是和中斷處理器關聯的。在中斷號共用的情況下核心如何正確找到對應的中斷處理器呢?核心採用一種最簡單的方法,就是不管三七二一,當同一中斷號的中斷髮生時,與該中斷號關聯的所有中斷處理器都一起被調用。調用後再靠中斷處理器中的過濾程式來篩選執行真正的中斷處理。

對於使用共用中斷號的裝置,它的驅動程式在註冊時必須先指明允許中斷共用。

IRQ與中斷處理器的映射關係儲存在一個向量表中。該表儲存了每個IRQ的中斷處理器。向量表的大小是平台相關的,從15(i386)到超過200都有。 irqaction資料結構儲存了映射表的資訊。上面提到的request_irq()函數建立irqaction資料結構並通過setup_irq()把它加入到irq_des向量表中。irq_des在 kernel/irq/handler.c中定義,平台相關的定義在arch/XXX/kernel/irq.c檔案中。setup_irq()在kernel/irq/manage.c,平台相關的定義在arch/XXX/kernel/irq.c中。

7.6. 網路裝置操作層的初始化
在系統啟動階段,網路裝置操作層通過net_dev_init()進行初始化。net_dev_init()的代碼在net/core/dev.c檔案中。這是一個以__init標識的函數,表示它是一個低層的代碼。

net_dev_init()的主要初始化工作內容包括以下幾點:

·         產生/proc/net目錄和目錄下相關的檔案。

7.7. 核心模組載入器
kmod是核心模組載入器。該載入器在系統啟動時會觸發/sbin/modprobe和/sbin/hotplug自動載入相應的核心模組和運行裝置啟動指令碼。modprobe使用/etc/modprobe.conf設定檔。當該檔案中有"alias eth0 3c59x"配置時就會自動加3c59x.ko模組。

7.8. 虛擬設備
虛擬設備是在真實裝置上的虛擬,虛擬設備和真實裝置的對應關係可以一對多或多對一。即一個虛擬設備對應多個真實裝置或多個真實裝置一個虛擬設備。下面介紹網路子系統中虛擬設備的應用情況。

·         Bonding,把多個真實網卡虛擬成一個虛擬網卡。對於應用來講就相當於訪問一個網路介面。

·         802.1Q,802.3乙太網路幀頭擴充,添加了VLAN頭資訊。把多個真實網卡虛擬成一個虛擬網卡。

·         Bridging,一個虛擬橋接器,把多個真實網卡虛擬成一個虛擬網卡。

·         Tunnel interfaces,實現GRE和IP-over-IP虛擬通道。把一個真實網卡虛擬成多個虛擬網卡。

·         True equalizer (TEQL),類似於Bonding。

上面不是一個完整列表,隨著核心的不斷開發完善,新功能新應用也會不斷出現。

7.9. 8139too.c源碼分析
程式調用流程:

module_init(rtl8139_init_module)
 
static int __init rtl8139_init_module (void)
 
pci_register_driver(&rtl8139_pci_driver)                  #註冊驅動程式
 
static int __devinit rtl8139_init_one (struct pci_dev *pdev,
                                       const struct pci_device_id *ent)
 
static int __devinit rtl8139_init_board (struct pci_dev *pdev,
                                         struct net_device **dev_out)
 
dev = alloc_etherdev (sizeof (*tp))         #為裝置分配net_device資料結構
 
pci_enable_device (pdev)        #啟用PCI裝置
 
pci_resource_start (pdev, 0)    #擷取PCI I/O地區1的首地址
pci_resource_end (pdev, 0)      #擷取PCI I/O地區1的尾地址
pci_resource_flags (pdev, 0)    #擷取PCI I/O地區1資源標記
pci_resource_len (pdev, 0)      #擷取地區資源長度
 
pci_resource_start (pdev, 1)    #擷取PCI I/O地區2的首地址
pci_resource_end (pdev, 1)      #擷取PCI I/O地區2的尾地址
pci_resource_flags (pdev, 1)    #擷取PCI I/O地區2資源標記
pci_resource_len (pdev, 1)      #擷取地區資源長度
 
pci_request_regions(pdev, DRV_NAME)     #檢查其它PCI裝置是否使用了相同的地址資源
 
pci_set_master(pdev)      #通過設定PCI裝置的命令寄存器允許DMA
7.10. 核心網路資料流
網路報文從應用程式產生,通過網卡發送,在另一端的網卡接收資料並傳遞給應用程式。這個過程網路報文在核心中調用了一系列的函數。下面把這些函數列舉出來,方便我們瞭解網路報文的流程。

發送流程:

write
  |
sys_write
  |
sock_sendmsg
  |
inet_sendmsg
  |
tcp_sendmsg
  |
tcp_push_one
  |
tcp_transmit_skb
  |
ip_queue_xmit
  |
ip_route_output
  |
ip_queue_xmit
  |
ip_queue_xmit2
  |
ip_output
  |
ip_finish_output
  |
neith_connected_output
  |
dev_queue_xmit ----------------|
  |                            |
  |                           queue_run
  |                           queue_restart
  |                            |
hard_start_xmit-----------------
接收流程:

netif_rx
  |
netif_rx_schedule
  |
_cpu_raise_softirq
  |
net_rx_action
  |
ip_rcv
  |
ip_rcv_finish
  |
ip_route_input
  |
ip_local_deliver
  |
ip_local_deliver_finish
  |
tcp_v4_rcv
  |
tcp_v4_do_rcv
  |
tcp_rcv_established------------------|
  |                                  |
tcp_data_queue                       |
  |                                  |
_skb_queue_tail----------------------|
  |
data_ready
  |
sock_def_readable
  |
wake_up_interruptible
  |
tcp_data_wait
  |
tcp_recvmsg
  |
inet_recvmsg
  |
sock_recvmsg
  |
sock_read
  |  
read

  
 
資料包在應用程式層稱為data,在TCP層稱為segment,在IP層稱為packet,在資料連結層稱為frame。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.