這篇文章主要介紹網絡層的數據是怎么通過接口層將數據發送出去的,在開始之前我們還是再來看一下softnet_data這個很重要的結構體:
struct softnet_data {
struct list_head poll_list;
struct sk_buff_head process_queue;
/* stats */
unsigned int processed;
unsigned int time_squeeze;
unsigned int received_rps;
#ifdef CONFIG_RPS
struct softnet_data *rps_ipi_list;
#endif
#ifdef CONFIG_NET_FLOW_LIMIT
struct sd_flow_limit __rcu *flow_limit;
#endif
struct Qdisc *output_queue;
struct Qdisc **output_queue_tailp;
struct sk_buff *completion_queue;
...
在數據包輸出的時候,其中比較重要的成員是output_queue和completion_queue, 前者是等待發送的隊列,后者是完成發送等待釋放的隊列,比如可能在等待ACK等。
下面我們來了解一下接口處數據包發送的基本流程:
下面這個圖是kernel2.6版本dev_queue_xmit()的處理流程,新的kernel已經有很多不一樣了,但是可以作為參考
關於接口層數據包的發送,我自己對這一塊也不是有太多的興趣,但是在網上找到幾篇介紹的比較清楚的文章,這里就直接貼過來,不浪費口舌了:
新kernel的代碼可能剛開始不是那么好看懂,可以先了解一下舊kernel的代碼然后看新的代碼會容易些。
這篇文章沒有詳細的介紹發送過程,但是需要記住一個比較重要的函數dev_hard_start_xmit,它是將數據包交給網卡驅動進行發送的接口:
dev_hard_start_xmit
繼續看dev_hard_start_xmit,這個函數比較簡單,調用xmit_one來發送一個到多個數據包了
structsk_buff *dev_hard_start_xmit(structsk_buff *first,structnet_device *dev,
structnetdev_queue *txq,int*ret)
{
structsk_buff *skb = first;
intrc = NETDEV_TX_OK;
/*此處skb為什么會有鏈表呢?*/
while(skb) {
/*取出skb的下一個數據單元*/
structsk_buff *next = skb->next;
/*置空,待發送數據包的next*/
skb->next = NULL;
/*將此數據包送到driver Tx函數,因為dequeue的數據也會從這里發送,所以會有netx!*/
rc = xmit_one(skb, dev, txq, next != NULL);
/*如果發送不成功,next還原到skb->next 退出*/
if(unlikely(!dev_xmit_complete(rc))) {
skb->next = next;
gotoout;
}
/*如果發送成功,把next置給skb,一般的next為空 這樣就返回,如果不為空就繼續發!*/
skb = next;
/*如果txq被stop,並且skb需要發送,就產生TX Busy的問題!*/
if(netif_xmit_stopped(txq) && skb) {
rc = NETDEV_TX_BUSY;
break;
}
}
out:
*ret = rc;
returnskb;
}
對於xmit_one這個來講比較簡單了,下面代碼中列出了xmit_one, netdev_start_xmit,__netdev_start_xmit 這個三個函數,其目的就是將封包送到driver的tx函數了..中間在送往driver之前,還會經歷抓包的過程,本文不介紹抓包的流程了。
staticintxmit_one(structsk_buff *skb,structnet_device *dev,
structnetdev_queue *txq,boolmore)
{
unsigned intlen;
intrc;
/*如果有抓包的工具的話,這個地方會進行抓包,such as Tcpdump*/
if(!list_empty(&ptype_all))
dev_queue_xmit_nit(skb, dev);
len = skb->len;
trace_net_dev_start_xmit(skb, dev);
/*調用netdev_start_xmit,快到driver的tx函數了*/
rc = netdev_start_xmit(skb, dev, txq, more);
trace_net_dev_xmit(skb, rc, dev, len);
returnrc;
}
staticinlinenetdev_tx_t netdev_start_xmit(structsk_buff *skb,structnet_device *dev,
structnetdev_queue *txq,boolmore)
{
conststructnet_device_ops *ops = dev->netdev_ops;
intrc;
/*__netdev_start_xmit 里面就完全是使用driver 的ops去發包了,其實到此為止,一個skb已經從netdevice
*這個層面送到driver層了,接下來會等待driver的返回*/
rc = __netdev_start_xmit(ops, skb, dev, more);
/*如果返回NETDEV_TX_OK,那么會更新下Txq的trans時間戳哦,txq->trans_start = jiffies;*/
if(rc == NETDEV_TX_OK)
txq_trans_update(txq);
returnrc;
}
staticinlinenetdev_tx_t __netdev_start_xmit(conststructnet_device_ops *ops,
structsk_buff *skb,structnet_device *dev,
boolmore)
{
skb->xmit_more = more ? 1 : 0;
returnops->ndo_start_xmit(skb, dev);
}
其中ops->ndo_start_xmit就是driver注冊的發包函數,這樣數據包就交給了驅動處理。
接口層的分析到這里告一段落,如果想要了解更多關於接口層的東西可以訪問這里
后面我們將進入廣闊的網絡層以及傳輸層的分析: