原文地址:TCP/IP学习(36)——IP包的发送流程(3) 作者:GFree_Wind
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以*拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
前文已经学习到了sch_direct_xmit函数,下面进入下一级函数。
- int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
- struct netdev_queue *txq)
-
{
- const struct net_device_ops *ops = dev->netdev_ops;
- int rc = NETDEV_TX_OK;
- if (likely(!skb->next)) {
/* skb->next为null,即只发送一个skb */
- if (!list_empty(&ptype_all))
- dev_queue_xmit_nit(skb, dev); //ptype_all上的协议处理,如tcpdump
- /*
- * If device doesnt need skb->dst, release it right now while
- * its hot in this cpu cache
- */
- /* device 不需要dst */
- if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
- skb_dst_drop(skb);
/* 使该skb不属于任何一个socket */
- skb_orphan_try(skb);
- if (netif_needs_gso(dev, skb)) {
- /* 需要scatter gather功能 */
/*
从注释上看,是进行perform protocol segmentation on skb。
但是实际工作,目前我不清楚
*/
- if (unlikely(dev_gso_segment(skb)))
- goto out_kfree_skb;
- if (skb->next)
- goto gso;
- } else {
- //不需要scatter gather
/*
如需要连续内存,则将skb内存线性化,即连续内存
*/
- if (skb_needs_linearize(skb, dev) &&
- __skb_linearize(skb))
- goto out_kfree_skb;
- /* If packet is not checksummed and device does not
- * support checksumming for this protocol, complete
- * checksumming here.
- */
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- /* 计算checksum */
- skb_set_transport_header(skb, skb->csum_start -
- skb_headroom(skb));
- if (!dev_can_checksum(dev, skb) &&
- skb_checksum_help(skb))
- goto out_kfree_skb;
- }
- }
/* 发送skb数据 */
- rc = ops->ndo_start_xmit(skb, dev);
- if (rc == NETDEV_TX_OK)
- txq_trans_update(txq);
- return rc;
- }
- gso:
- /* 循环发送数据包 */
- do {
- struct sk_buff *nskb = skb->next;
- skb->next = nskb->next;
- nskb->next = NULL;
- /*
- * If device doesnt need nskb->dst, release it right now while
- * its hot in this cpu cache
- */
- if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
- skb_dst_drop(nskb);
- rc = ops->ndo_start_xmit(nskb, dev);
- if (unlikely(rc != NETDEV_TX_OK)) {
- if (rc & ~NETDEV_TX_MASK)
- goto out_kfree_gso_skb;
- nskb->next = skb->next;
- skb->next = nskb;
- return rc;
- }
- txq_trans_update(txq);
- if (unlikely(netif_tx_queue_stopped(txq) && skb->next))
- return NETDEV_TX_BUSY;
- } while (skb->next);
- out_kfree_gso_skb:
- if (likely(skb->next == NULL))
- skb->destructor = DEV_GSO_CB(skb)->destructor;
- out_kfree_skb:
- kfree_skb(skb);
- return rc;
- }
继续下一个函数,ops->ndo_start_xmit。又是一个回调函数,这个回调函数由驱动所决定。以e1000_main.c为例,
- static const struct net_device_ops e1000_netdev_ops = {
- .ndo_open = e1000_open,
- .ndo_stop = e1000_close,
- .ndo_start_xmit = e1000_xmit_frame,
- .ndo_get_stats = e1000_get_stats,
- .ndo_set_rx_mode = e1000_set_rx_mode,
- .ndo_set_mac_address = e1000_set_mac,
- .ndo_tx_timeout = e1000_tx_timeout,
- .ndo_change_mtu = e1000_change_mtu,
- .ndo_do_ioctl = e1000_ioctl,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_vlan_rx_register = e1000_vlan_rx_register,
- .ndo_vlan_rx_add_vid = e1000_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = e1000_vlan_rx_kill_vid,
- #ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = e1000_netpoll,
- #endif
- };
从这里可以看出,对于e1000_main来说,该回调函数为e1000_xmit_frame。这个涉及到驱动层,一般都与硬件相关,在此就不继续探讨了。
那么现在,一个基本的IP包发送流程,基本上浏览完毕。小小的总结一下,当linux发送IP数据包时,数据包有可能是立刻发送出去,也有可能由下一次的软中断发送。