歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

linux協議棧之鏈路層上的數據傳輸

<<prison break>>第三季的第五集,終於在翹首企盼中姗姗來遲了,scofid用它驚人的智慧一次次化險為夷,但在邪惡的sona監獄他將如何逃脫呢?這我們不得而知,但我們可以分析Linux網絡驅動來得到數據包是怎麼通過物理接口的這一層“prison”束縛來達到通信目的:-)

一:預備知識

關於I/O內存映射。

設備通過控制總線,數據總線,狀態總線與CPU相連。控制總數傳送控制信號,例如,網卡的啟用。數據總線控制數據傳輸,例如,網卡發送數據,狀態總數一般都是讀取設備的當前狀態,例如讀取網卡的MAC地址。

在傳統的操作中,都是通過讀寫設備寄存器的值來實現。但是這樣耗費了CPU時鐘。而且每取一次值都要讀取設備寄存器,造成了效率的低下。在現代操作系統中。引用了I/O內存映射。即把寄存器的值映身到主存。對設備寄存器的操作,轉換為對主存的操作,這樣極大的提高了效率。

關於DMA

這是關於設備數據處理的一種方式。傳統的處理方法為:當設備接收到數據,向CPU報告中斷。CPU處理中斷,把數據放到內存。

在現代操作系統中引入的DMA是指,設備接收到數據時,把數據放至DMA內存,再向CPU產生中斷。這樣節省了大量的CPU時間

關於軟中斷與NAPI

在現代操作系統中,對中斷的處理速度要求越來越高。為了響應中斷,將中斷分為兩部份,即上半部與下半部。上半部將數據推入處理隊列,響應中斷。然後再由下半部調度完成余下的任務。

NAPI是2.6新引入的一個概念,它在發生中斷的時候,禁用中斷。然後處理數據。之後,每隔一定的時候,它會主動向設備詢用是否有數據要處理。

I/O,DMA在後續代碼分析中會討論在linux2.6.21中的實現。軟中斷與NAPI的詳細知識將會在分析中斷處理的時候,一一為你道來
二:從網卡驅動說起。

以intel 100M 網卡驅動為例簡要概述數據包的接收與發送流程。代碼見(drivers/net/e100.c)

網卡是屬於PCI設備,它的注冊跟一般的PCI設備注冊沒什麼兩樣。

static int __init e100_init_module(void)

{

if(((1 << debug) - 1) & NETIF_MSG_DRV) {

printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);

printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT);

}



//注冊PCI

return pci_module_init(&e100_driver);

}

其中e100_driver對應為網卡的pci_driver.

static struct pci_driver e100_driver = {

//驅動對應的名字

.name = DRV_NAME,

//匹配類型

.id_table = e100_id_table,

//偵測函數

.probe = e100_probe,

//移除函數,設備移除時將調用此函數

.remove = __devexit_p(e100_remove),

#ifdef CONFIG_PM

.suspend = e100_suspend,

.resume = e100_resume,

#endif

}

當總數探測到PCI設備符合e100_id_table中的參數時,將會調用e100_probe,開始設備的初始化

在e100_probe中:

static int __devinit e100_probe(struct pci_dev *pdev,

const struct pci_device_id *ent)

{

struct net_device *netdev;

struct nic *nic;

int err;



//分配net_device並為其賦值

//alloc_etherdev為以太網接口的net_device分配函數。它是alloc_netdev的封裝函數

if(!(netdev = alloc_etherdev(sizeof(struct nic)))) {

if(((1 << debug) - 1) & NETIF_MSG_PROBE)

printk(KERN_ERR PFX "Etherdev alloc failed, abort.\n");

return -ENOMEM;

}

 

//對netdev中的函數指針賦初值

netdev->open = e100_open;

netdev->stop = e100_close;

netdev->hard_start_xmit = e100_xmit_frame;

netdev->get_stats = e100_get_stats;

netdev->set_multicast_list = e100_set_multicast_list;

netdev->set_mac_address = e100_set_mac_address;

netdev->change_mtu = e100_change_mtu;

netdev->do_ioctl = e100_do_ioctl;

//支持ethtool工具時有效

SET_ETHTOOL_OPS(netdev, &e100_ethtool_ops);

netdev->tx_timeout = e100_tx_timeout;

netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;

//輪詢函數

netdev->poll = e100_poll;

netdev->weight = E100_NAPI_WEIGHT;

#ifdef CONFIG_NET_POLL_CONTROLLER

netdev->poll_controller = e100_netpoll;

#endif



//獲得net_device私有數據區,並對其賦值

//私有數據大小是由alloc_etherdev()參數中指定的

nic = netdev_priv(netdev);

nic->netdev = netdev;

nic->pdev = pdev;

nic->msg_enable = (1 << debug) - 1;

pci_set_drvdata(pdev, netdev);



//啟動網卡.為之後DMA,I/O內存映射做准備

//它實際上是對PCI的控制寄存器賦值來實現的

if((err = pci_enable_device(pdev))) {

DPRINTK(PROBE, ERR, "Cannot enable PCI device, aborting.\n");

Copyright © Linux教程網 All Rights Reserved