学习Linux-4.12内核网路协议栈(2.4)——接口层数据包的发送

来源:互联网 发布:农村淘宝合伙人加盟 编辑:程序博客网 时间:2024/05/22 00:31

这篇文章主要介绍网络层的数据是怎么通过接口层将数据发送出去的,在开始之前我们还是再来看一下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已经有很多不一样了,但是可以作为参考



关于接口层数据包的发送,我自己对这一块也不是有太多的兴趣,但是在网上找到几篇介绍的比较清楚的文章,这里就直接贴过来,不浪费口舌了:

Linux网络之设备接口层:发送数据包流程dev_queue_xmit

Linux网络之设备接口层:发送数据包流程dev_queue_xmit(二)

dev_queue_xmi函数详解

新kernel的代码可能刚开始不是那么好看懂,可以先了解一下旧kernel的代码然后看新的代码会容易些。


这篇文章没有详细的介绍发送过程,但是需要记住一个比较重要的函数dev_hard_start_xmit,它是将数据包交给网卡驱动进行发送的接口:

dev_hard_start_xmit

继续看dev_hard_start_xmit,这个函数比较简单,调用xmit_one来发送一个到多个数据包了

[cpp] view plain copy
  1. struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,  
  2.                     struct netdev_queue *txq, int *ret)  
  3. {  
  4.     struct sk_buff *skb = first;  
  5.     int rc = NETDEV_TX_OK;  
  6.        /*此处skb为什么会有链表呢?*/  
  7.     while (skb) {  
  8.           /*取出skb的下一个数据单元*/  
  9.         struct sk_buff *next = skb->next;  
  10.               /*置空,待发送数据包的next*/  
  11.         skb->next = NULL;  
  12.           
  13.            /*将此数据包送到driver Tx函数,因为dequeue的数据也会从这里发送,所以会有netx!*/  
  14.         rc = xmit_one(skb, dev, txq, next != NULL);  
  15.           
  16.         /*如果发送不成功,next还原到skb->next 退出*/  
  17.         if (unlikely(!dev_xmit_complete(rc))) {  
  18.             skb->next = next;  
  19.             goto out;  
  20.         }  
  21.                 /*如果发送成功,把next置给skb,一般的next为空 这样就返回,如果不为空就继续发!*/  
  22.         skb = next;  
  23.           
  24.         /*如果txq被stop,并且skb需要发送,就产生TX Busy的问题!*/  
  25.         if (netif_xmit_stopped(txq) && skb) {  
  26.             rc = NETDEV_TX_BUSY;  
  27.             break;  
  28.         }  
  29.     }  
  30.   
  31. out:  
  32.     *ret = rc;  
  33.     return skb;  
  34. }  
对于xmit_one这个来讲比较简单了,下面代码中列出了xmit_one, netdev_start_xmit,__netdev_start_xmit 这个三个函数,其目的就是将封包送到driver的tx函数了..中间在送往driver之前,还会经历抓包的过程,本文不介绍抓包的流程了。

[cpp] view plain copy
  1. static int xmit_one(struct sk_buff *skb, struct net_device *dev,  
  2.             struct netdev_queue *txq, bool more)  
  3. {  
  4.     unsigned int len;  
  5.     int rc;  
  6.       
  7.     /*如果有抓包的工具的话,这个地方会进行抓包,such as Tcpdump*/  
  8.     if (!list_empty(&ptype_all))  
  9.         dev_queue_xmit_nit(skb, dev);  
  10.   
  11.     len = skb->len;  
  12.     trace_net_dev_start_xmit(skb, dev);  
  13.         /*调用netdev_start_xmit,快到driver的tx函数了*/  
  14.     rc = netdev_start_xmit(skb, dev, txq, more);  
  15.     trace_net_dev_xmit(skb, rc, dev, len);  
  16.   
  17.     return rc;  
  18. }  
  19.   
  20. static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_device *dev,  
  21.                         struct netdev_queue *txq, bool more)  
  22. {  
  23.     const struct net_device_ops *ops = dev->netdev_ops;  
  24.     int rc;  
  25.     /*__netdev_start_xmit 里面就完全是使用driver 的ops去发包了,其实到此为止,一个skb已经从netdevice 
  26.      *这个层面送到driver层了,接下来会等待driver的返回*/  
  27.     rc = __netdev_start_xmit(ops, skb, dev, more);  
  28.       
  29.     /*如果返回NETDEV_TX_OK,那么会更新下Txq的trans时间戳哦,txq->trans_start = jiffies;*/  
  30.     if (rc == NETDEV_TX_OK)  
  31.         txq_trans_update(txq);  
  32.   
  33.     return rc;  
  34. }  
  35.   
  36. static inline netdev_tx_t __netdev_start_xmit(const struct net_device_ops *ops,  
  37.                           struct sk_buff *skb, struct net_device *dev,  
  38.                           bool more)  
  39. {  
  40.     skb->xmit_more = more ? 1 : 0;  
  41.     return ops->ndo_start_xmit(skb, dev);  
  42. }  

其中ops->ndo_start_xmit就是driver注册的发包函数,这样数据包就交给了驱动处理。


接口层的分析到这里告一段落,如果想要了解更多关于接口层的东西可以访问这里

了解linux网络协议栈(三)——协议栈实现


后面我们将进入广阔的网络层以及传输层的分析:





阅读全文
0 0
原创粉丝点击