帧的发送

简介:       上一节我们说了帧的接收,自然有收就有发,并且也很少说关于发送的东西,这里我们就分析下帧的发送.      参考内核2.6.32.60   net/core/dev.c        我们先看看设备无关层的经典发送函数接口 dev_queue_xmit,当然这里要说一下...
      上一节我们说了帧的接收,自然有收就有发,并且也很少说关于发送的东西,这里我们就分析下帧的发送.
     参考内核2.6.32.60   net/core/dev.c  
     我们先看看设备无关层的经典发送函数接口 dev_queue_xmit,当然这里要说一下,上层是如何调用到这里的,当然数据发送到ip层的时候,会调用到邻居子系统模块会把这个接口给联系起来,因为要给数据包封装帧头.查询arp表,找到ip和mac的对应关系当然还有接口的.

点击(此处)折叠或打开

  1. /* This function can be used in contexts, where only old dev_queue_xmit
  2.    worked, f.e. if you want to override normal output path (eql, shaper),
  3.    but resolution is not made yet.
  4.  */

  5. int neigh_compat_output(struct sk_buff *skb)
  6. {
  7.     struct net_device *dev = skb->dev;

  8.     __skb_pull(skb, skb_network_offset(skb));

  9.     if (dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL,
  10.              skb->len) 0 &&
  11.      dev->header_ops->rebuild(skb))
  12.         return 0;

  13.     return dev_queue_xmit(skb);
  14. }
这里不多说上层的流程.

点击(此处)折叠或打开

  1. /**
  2.  *    dev_queue_xmit - transmit a buffer
  3.  *    @skb: buffer to transmit
  4.  *
  5.  *    Queue a buffer for transmission to a network device. The caller must
  6.  *    have set the device and priority and built the buffer before calling
  7.  *    this function. The function can be called from an interrupt.
  8.  *
  9.  *    A negative errno code is returned on a failure. A success does not
  10.  *    guarantee the frame will be transmitted as it may be dropped due
  11.  *    to congestion or traffic shaping.
  12.  *
  13.  * -----------------------------------------------------------------------------------
  14.  * I notice this method can also return errors from the queue disciplines,
  15.  * including NET_XMIT_DROP, which is a positive value. So, errors can also
  16.  * be positive.
  17.  *
  18.  * Regardless of the return value, the skb is consumed, so it is currently
  19.  * difficult to retry a send to this method. (You can bump the ref count
  20.  * before sending to hold a reference for retry if you are careful.)
  21.  *
  22.  * When calling this method, interrupts MUST be enabled. This is because
  23.  * the BH enable code must have IRQs enabled so that it will not deadlock.
  24.  * --BLG
  25.  */
  26. int dev_queue_xmit(struct sk_buff *skb)
  27. {
  28.     struct net_device *dev = skb->dev;
  29.     struct netdev_queue *txq;
  30.     struct Qdisc *q;
  31.     int rc = -ENOMEM;

  32.     /* GSO will handle the following emulations directly. */
  33.     if (netif_needs_gso(dev, skb))     //帧的聚合/分散  用来提高网络性能 可以参考tso,如果有了支持TSO的网卡,CPU可以直接将要发送的大数据发送到网卡上                                       //,由网卡硬件去负责分片和计算校验和;同时也需要支持硬件校验和 
  34.         goto gso;

  35.     if (skb_has_frags(skb) &&              //这里判断帧是否有分片,如果有把分片的部分,组合到一起发送.
  36.      !(dev->features & NETIF_F_FRAGLIST) &&
  37.      __skb_linearize(skb))
  38.         goto out_kfree_skb;

  39.     /* Fragmented skb is linearized if device does not support SG,
  40.      * or if at least one of fragments is in highmem and device
  41.      * does not support DMA from it.
  42.      */
  43.     if (skb_shinfo(skb)->nr_frags &&
  44.      (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
  45.      __skb_linearize(skb))
  46.         goto out_kfree_skb;

  47.     /* If packet is not checksummed and device does not support
  48.      * checksumming for this protocol, complete checksumming here.
  49.      */
  50.     if (skb->ip_summed == CHECKSUM_PARTIAL) {                 // 软件 校验L4 校验和,然后准备发送帧.
  51.         skb_set_transport_header(skb, skb->csum_start -
  52.                      skb_headroom(skb));
  53.         if (!dev_can_checksum(dev, skb) && skb_checksum_help(skb))
  54.             goto out_kfree_skb;
  55.     }

  56. gso:
  57.     /* Disable soft irqs for various locks below. Also
  58.      * stops preemption for RCU.
  59.      */
  60.     rcu_read_lock_bh();

  61.     txq = dev_pick_tx(dev, skb);
  62.     q = rcu_dereference(txq->qdisc);

  63. #ifdef CONFIG_NET_CLS_ACT                // 这里默认开启了这个选项,即qos,流量控制 
  64.     skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
  65. #endif
  66.     if (q->enqueue) {
  67.         rc = __dev_xmit_skb(skb, q, dev, txq);
  68.         goto out;
  69.     }

  70.     /* The device has no queue. Common case for software devices:
  71.      loopback, all the sorts of tunnels...

  72.      Really, it is unlikely that netif_tx_lock protection is necessary
  73.      here. (f.e. loopback and IP tunnels are clean ignoring statistics
  74.      counters.)
  75.      However, it is possible, that they rely on protection
  76.      made by us here.

  77.      Check this and shot the lock. It is not prone from deadlocks.
  78.      Either shot noqueue qdisc, it is even simpler 8)
  79.      */
  80.     if (dev->flags & IFF_UP) {
  81.         int cpu = smp_processor_id(); /* ok because BHs are off */

  82.         if (txq->xmit_lock_owner != cpu) {

  83.             HARD_TX_LOCK(dev, txq, cpu);

  84.             if (!netif_tx_queue_stopped(txq)) {
  85.                 rc = NET_XMIT_SUCCESS;
  86.                 if (!dev_hard_start_xmit(skb, dev, txq)) {
  87.                     HARD_TX_UNLOCK(dev, txq);
  88.                     goto out;
  89.                 }
  90.             }
  91.             HARD_TX_UNLOCK(dev, txq);
  92.             if (net_ratelimit())
  93.                 printk(KERN_CRIT "Virtual device %s asks to "
  94.                  "queue packet!\n", dev->name);
  95.         } else {
  96.             /* Recursion is It is possible,
  97.              * unfortunately */
  98.             if (net_ratelimit())
  99.                 printk(KERN_CRIT "Dead loop on virtual device "
  100.                  "%s, fix it urgently!\n", dev->name);
  101.         }
  102.     }

  103.     rc = -ENETDOWN;
  104.     rcu_read_unlock_bh();

  105. out_kfree_skb:
  106.     kfree_skb(skb);
  107.     return rc;
  108. out:
  109.     rcu_read_unlock_bh();
  110.     return rc;
  111. }
我们这里说一下q - >enqueue ,它到底为真还是空呢?

点击(此处)折叠或打开

  1. txq = dev_pick_tx(dev, skb);
  2.     q = rcu_dereference(txq->qdisc);

点击(此处)折叠或打开

  1. static struct netdev_queue *dev_pick_tx(struct net_device *dev,
  2.                     struct sk_buff *skb)
  3. {
  4.     const struct net_device_ops *ops = dev->netdev_ops;
  5.     u16 queue_index = 0;

  6.     if (ops->ndo_select_queue)
  7.         queue_index = ops->ndo_select_queue(dev, skb);
  8.     else if (dev->real_num_tx_queues > 1)
  9.         queue_index = skb_tx_hash(dev, skb);

  10.     skb_set_queue_mapping(skb, queue_index);
  11.     return netdev_get_tx_queue(dev, queue_index);
  12. }
这里很明显是获取tx qdisc. 而ops我们如果看过网卡驱动,就知道默认很少有人去初始化.ndo_select_queue,
而dev - >real_num_tx_queues在前面文章中我们知道默认初始化为1. 所以这个函数的返回值是:
&dev->_tx[index]; // 而index明显是0 . 也是说是_tx数组的第一个元素.
我们回想到设备注册函数中的dev_init_scheduler就明白了.默认初始化qdisc是noop_qdisc
在sch_generic.c中

点击(此处)折叠或打开

  1. struct Qdisc noop_qdisc = {
  2.     .enqueue    =    noop_enqueue,
  3.     .dequeue    =    noop_dequeue,
  4.     .flags        =    TCQ_F_BUILTIN,
  5.     .ops        =    &noop_qdisc_ops,
  6.     .list        =    LIST_HEAD_INIT(noop_qdisc.list),
  7.     .q.lock        =    __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock),
  8.     .dev_queue    =    &noop_netdev_queue,
  9. }
当然后来,在dev_open的时候,里面有

点击(此处)折叠或打开

  1. /*
  2.          *    Wakeup transmit queue engine
  3.          */
  4.         dev_activate(dev);
又重新初始化了.

点击(此处)折叠或打开

  1. void dev_activate(struct net_device *dev)
  2. {
  3.     int need_watchdog;

  4.     /* No queueing discipline is attached to device;
  5.      create default one i.e. pfifo_fast for devices,
  6.      which need queueing and noqueue_qdisc for
  7.      virtual interfaces
  8.      */

  9.     if (dev->qdisc == &noop_qdisc)
  10.         attach_default_qdiscs(dev);

  11.     if (!netif_carrier_ok(dev))
  12.         /* Delay activation until next carrier-on event */
  13.         return;

  14.     need_watchdog = 0;
  15.     netdev_for_each_tx_queue(dev, transition_one_qdisc, &need_watchdog);
  16.     transition_one_qdisc(dev, &dev->rx_queue, NULL);

  17.     if (need_watchdog) {
  18.         dev->trans_start = jiffies;
  19.         dev_watchdog_up(dev);
  20.     }
  21. }
我们看看 attach_default_qdiscs (dev ) ;

点击(此处)折叠或打开

  1. static void attach_default_qdiscs(struct net_device *dev)
  2. {
  3.     struct netdev_queue *txq;
  4.     struct Qdisc *qdisc;

  5.     txq = netdev_get_tx_queue(dev, 0);

  6.     if (!netif_is_multiqueue(dev) || dev->tx_queue_len == 0) {
  7.         netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
  8.         dev->qdisc = txq->qdisc_sleeping;
  9.         atomic_inc(&dev->qdisc->refcnt);
  10.     } else {
  11.         qdisc = qdisc_create_dflt(dev, txq, &mq_qdisc_ops, TC_H_ROOT);
  12.         if (qdisc) {
  13.             qdisc->ops->attach(qdisc);
  14.             dev->qdisc = qdisc;
  15.         }
  16.     }
  17. }
这里有初始化为了mq_qdisc_ops.而它的enqueue函数是:
pfifo_enqueue://sch_fifo.c 

点击(此处)折叠或打开

  1. static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch)
  2. {
  3.     struct fifo_sched_data *q = qdisc_priv(sch);

  4.     if (likely(skb_queue_len(&sch->q) q->limit))
  5.         return qdisc_enqueue_tail(skb, sch);

  6.     return qdisc_reshape_fail(skb, sch);
  7. }
所以会进入:

点击(此处)折叠或打开

  1. static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
  2.                  struct net_device *dev,
  3.                  struct netdev_queue *txq)
  4. {
  5.     spinlock_t *root_lock = qdisc_lock(q);
  6.     int rc;

  7.     spin_lock(root_lock);
  8.     if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {  // qdisc 没有激活,当然不可能了,前面dev_open的时候~。~
  9.         kfree_skb(skb);
  10.         rc = NET_XMIT_DROP;
  11.     } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&     //  预留的一些操作,暂时我也没看懂,但是从qdis_qlen可以知道qlen为空才会进入....
  12.          !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) {
  13.         /*
  14.          * This is a work-conserving queue; there are no old skbs
  15.          * waiting to be sent out; and the qdisc is not running -
  16.          * xmit the skb directly.
  17.          */
  18.         __qdisc_update_bstats(q, skb->len);
  19.         if (sch_direct_xmit(skb, q, dev, txq, root_lock))
  20.             __qdisc_run(q);
  21.         else
  22.             clear_bit(__QDISC_STATE_RUNNING, &q->state);

  23.         rc = NET_XMIT_SUCCESS;
  24.     } else { //正常进入这里.
  25.         rc = qdisc_enqueue_root(skb, q);
  26.         qdisc_run(q);
  27.     }
  28.     spin_unlock(root_lock);

  29.     return rc;
  30. }
qdisc_enqueue_root 会调用enqueue加入队列,然后调用qdisc_run .它设置队列状态为running然后调用:

点击(此处)折叠或打开

  1. void __qdisc_run(struct Qdisc *q)
  2. {
  3.     unsigned long start_time = jiffies;

  4.     while (qdisc_restart(q)) {
  5.         /*
  6.          * Postpone processing if
  7.          * 1. another process needs the CPU;
  8.          * 2. we've been doing it for too long.
  9.          */
  10.         if (need_resched() || jiffies != start_time) {
  11.             __netif_schedule(q);
  12.             break;
  13.         }
  14.     }

  15.     clear_bit(__QDISC_STATE_RUNNING, &q->state);
  16. }
这里先说下while内的代码. 看注释我们明白,需要延迟处理呗. 然后调用__netif_schedule。

点击(此处)折叠或打开

  1. void __netif_schedule(struct Qdisc *q)
  2. {
  3.     if (!test_and_set_bit(__QDISC_STATE_SCHED, &q->state))
  4.         __netif_reschedule(q);
  5. }

点击(此处)折叠或打开

  1. static inline void __netif_reschedule(struct Qdisc *q)
  2. {
  3.     struct softnet_data *sd;
  4.     unsigned long flags;

  5.     local_irq_save(flags);
  6.     sd = &__get_cpu_var(softnet_data);
  7.     q->next_sched = sd->output_queue;
  8.     sd->output_queue = q;
  9.     raise_softirq_irqoff(NET_TX_SOFTIRQ);
  10.     local_irq_restore(flags);
  11. }
看到上面的代码我们是否明白点什么,对,就是软中断发送中断的调用处. 当然关于rx软中断前面napi机制里已经说的很清楚了.

点击(此处)折叠或打开

  1. static void net_tx_action(struct softirq_action *h)
  2. {
  3.     struct softnet_data *sd = &__get_cpu_var(softnet_data);

  4.     if (sd->completion_queue) {   //已经发送完的报文,但是buff还没释放
  5.         struct sk_buff *clist;

  6.         local_irq_disable();
  7.         clist = sd->completion_queue;
  8.         sd->completion_queue = NULL;
  9.         local_irq_enable();

  10.         while (clist) {
  11.             struct sk_buff *skb = clist;
  12.             clist = clist->next;

  13.             WARN_ON(atomic_read(&skb->users));
  14.             __kfree_skb(skb);
  15.         }
  16.     }

  17.     if (sd->output_queue) {
  18.         struct Qdisc *head;

  19.         local_irq_disable();
  20.         head = sd->output_queue;
  21.         sd->output_queue = NULL;
  22.         local_irq_enable();

  23.         while (head) {
  24.             struct Qdisc *q = head;
  25.             spinlock_t *root_lock;

  26.             head = head->next_sched;

  27.             root_lock = qdisc_lock(q);
  28.             if (spin_trylock(root_lock)) {
  29.                 smp_mb__before_clear_bit();
  30.                 clear_bit(__QDISC_STATE_SCHED,
  31.                      &q->state);
  32.                 qdisc_run(q);
  33.                 spin_unlock(root_lock);
  34.             } else {
  35.                 if (!test_bit(__QDISC_STATE_DEACTIVATED,
  36.                      &q->state)) {
  37.                     __netif_reschedule(q);
  38.                 } else {
  39.                     smp_mb__before_clear_bit();
  40.                     clear_bit(__QDISC_STATE_SCHED,
  41.                          &q->state);
  42.                 }
  43.             }
  44.         }
  45.     }
  46. }
在处理延迟的output_queue时,最后又调用到qdisc_run. 本质都会调用 qdisc_restart. 直到数据发送完.

点击(此处)折叠或打开

  1. /*
  2.  * NOTE: Called under qdisc_lock(q) with locally disabled BH.
  3.  *
  4.  * __QDISC_STATE_RUNNING guarantees only one CPU can process
  5.  * this qdisc at a time. qdisc_lock(q) serializes queue accesses for
  6.  * this queue.
  7.  *
  8.  * netif_tx_lock serializes accesses to device driver.
  9.  *
  10.  * qdisc_lock(q) and netif_tx_lock are mutually exclusive,
  11.  * if one is grabbed, another must be free.
  12.  *
  13.  * Note, that this procedure can be called by a watchdog timer
  14.  *
  15.  * Returns to the caller:
  16.  *                0 - queue is empty or throttled.
  17.  *                >0 - queue is not empty.
  18.  *
  19.  */
  20. static inline int qdisc_restart(struct Qdisc *q)
  21. {
  22.     struct netdev_queue *txq;
  23.     struct net_device *dev;
  24.     spinlock_t *root_lock;
  25.     struct sk_buff *skb;

  26.     /* Dequeue packet */
  27.     skb = dequeue_skb(q);
  28.     if (unlikely(!skb))
  29.         return 0;

  30.     root_lock = qdisc_lock(q);
  31.     dev = qdisc_dev(q);
  32.     txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));

  33.     return sch_direct_xmit(skb, q, dev, txq, root_lock);
  34. }
这里我们看到了dequeue函数的调用,它就是qdisc生效的地方,具体不多说.然后是直接 sch_direct_xmit发送

点击(此处)折叠或打开

  1. /*
  2.  * Transmit one skb, and handle the return status as required. Holding the
  3.  * __QDISC_STATE_RUNNING bit guarantees that only one CPU can execute this
  4.  * function.
  5.  *
  6.  * Returns to the caller:
  7.  *                0 - queue is empty or throttled.
  8.  *                >0 - queue is not empty.
  9.  */
  10. int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
  11.          struct net_device *dev, struct netdev_queue *txq,
  12.          spinlock_t *root_lock)
  13. {
  14.     int ret = NETDEV_TX_BUSY;

  15.     /* And release qdisc */
  16.     spin_unlock(root_lock);

  17.     HARD_TX_LOCK(dev, txq, smp_processor_id());
  18.     if (!netif_tx_queue_stopped(txq) &&
  19.      !netif_tx_queue_frozen(txq))
  20.         ret = dev_hard_start_xmit(skb, dev, txq);
  21.     HARD_TX_UNLOCK(dev, txq);

  22.     spin_lock(root_lock);

  23.     switch (ret) {
  24.     case NETDEV_TX_OK:
  25.         /* Driver sent out skb successfully */
  26.         ret = qdisc_qlen(q);
  27.         break;

  28.     case NETDEV_TX_LOCKED:
  29.         /* Driver try lock failed */
  30.         ret = handle_dev_cpu_collision(skb, txq, q);
  31.         break;

  32.     default:
  33.         /* Driver returned NETDEV_TX_BUSY - requeue skb */
  34.         if (unlikely (ret != NETDEV_TX_BUSY && net_ratelimit()))
  35.             printk(KERN_WARNING "BUG %s code %d qlen %d\n",
  36.              dev->name, ret, q->q.qlen);

  37.         ret = dev_requeue_skb(skb, q);
  38.         break;
  39.     }

  40.     if (ret && (netif_tx_queue_stopped(txq) ||
  41.          netif_tx_queue_frozen(txq)))
  42.         ret = 0;

  43.     return ret;
  44. }
这个函数有几个工作:
1.dev_hard_start_xmit  直接调用网卡驱动发送出去
2.根据第一步的返回值,继续处理,如果发送失败,又分处理
3.如果是locked ,那么就调用冲突处理
4.其他重新入队列继续发送.
基本又重新循环了,直到数据报文发送成功或者丢弃.

点击(此处)折叠或打开

  1. int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
  2.             struct netdev_queue *txq)
  3. {
  4.     const struct net_device_ops *ops = dev->netdev_ops;
  5.     int rc;

  6.     if (likely(!skb->next)) {
  7.         if (!list_empty(&ptype_all))
  8.             dev_queue_xmit_nit(skb, dev);    //  嗅探器

  9.         if (netif_needs_gso(dev, skb)) {
  10.             if (unlikely(dev_gso_segment(skb)))
  11.                 goto out_kfree_skb;
  12.             if (skb->next)
  13.                 goto gso;
  14.         }

  15.         /*
  16.          * If device doesnt need skb->dst, release it right now while
  17.          * its hot in this cpu cache
  18.          */
  19.         if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
  20.             skb_dst_drop(skb);

  21.         rc = ops->ndo_start_xmit(skb, dev);      //调用网卡驱动发送
  22.         if (rc == NETDEV_TX_OK)
  23.             txq_trans_update(txq);
  24.         /*
  25.          * TODO: if skb_orphan() was called by
  26.          * dev->hard_start_xmit() (for example, the unmodified
  27.          * igb driver does that; bnx2 doesn't), then
  28.          * skb_tx_software_timestamp() will be unable to send
  29.          * back the time stamp.
  30.          *
  31.          * How can this be prevented? Always create another
  32.          * reference to the socket before calling
  33.          * dev->hard_start_xmit()? Prevent that skb_orphan()
  34.          * does anything in dev->hard_start_xmit() by clearing
  35.          * the skb destructor before the call and restoring it
  36.          * afterwards, then doing the skb_orphan() ourselves?
  37.          */
  38.         return rc;
  39.     }

  40. gso:
  41.     do {
  42.         struct sk_buff *nskb = skb->next;

  43.         skb->next = nskb->next;
  44.         nskb->next = NULL;

  45.         /*
  46.          * If device doesnt need nskb->dst, release it right now while
  47.          * its hot in this cpu cache
  48.          */
  49.         if (dev->priv_flags & IFF_XMIT_DST_RELEASE)
  50.             skb_dst_drop(nskb);

  51.         rc = ops->ndo_start_xmit(nskb, dev);     //调用网卡驱动发送
  52.         if (unlikely(rc != NETDEV_TX_OK)) {
  53.             nskb->next = skb->next;
  54.             skb->next = nskb;
  55.             return rc;
  56.         }
  57.         txq_trans_update(txq);
  58.         if (unlikely(netif_tx_queue_stopped(txq) && skb->next))
  59.             return NETDEV_TX_BUSY;
  60.     } while (skb->next);

  61.     skb->destructor = DEV_GSO_CB(skb)->destructor;

  62. out_kfree_skb:
  63.     kfree_skb(skb);
  64.     return NETDEV_TX_OK;
  65. }
这个函数主要功能是如果设置了嗅探器,则 dev_queue_xmit_nit复制一份,这个在前面接收的时候也说到过,它这里查询ptype_all链表,注册的类型是eth_all

点击(此处)折叠或打开

  1. /*
  2.  *    Support routine. Sends outgoing frames to any network
  3.  *    taps currently in use.
  4.  */

  5. static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
  6. {
  7.     struct packet_type *ptype;

  8. #ifdef CONFIG_NET_CLS_ACT
  9.     if (!(skb->tstamp.tv64 && (G_TC_FROM(skb->tc_verd) & AT_INGRESS)))
  10.         net_timestamp(skb);
  11. #else
  12.     net_timestamp(skb);
  13. #endif

  14.     rcu_read_lock();
  15.     list_for_each_entry_rcu(ptype, &ptype_all, list) {
  16.         /* Never send packets back to the socket
  17.          * they originated from - MvS (miquels@drinkel.ow.org)
  18.          */
  19.         if ((ptype->dev == dev || !ptype->dev) &&
  20.          (ptype->af_packet_priv == NULL ||
  21.          (struct sock *)ptype->af_packet_priv != skb->sk)) {
  22.             struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
  23.             if (!skb2)
  24.                 break;

  25.             /* skb->nh should be correctly
  26.              set by sender, so that the second statement is
  27.              just protection against buggy protocols.
  28.              */
  29.             skb_reset_mac_header(skb2);

  30.             if (skb_network_header(skb2) skb2->data ||
  31.              skb2->network_header > skb2->tail) {
  32.                 if (net_ratelimit())
  33.                     printk(KERN_CRIT "protocol %04x is "
  34.                      "buggy, dev %s\n",
  35.                      skb2->protocol, dev->name);
  36.                 skb_reset_network_header(skb2);
  37.             }

  38.             skb2->transport_header = skb2->network_header;
  39.             skb2->pkt_type = PACKET_OUTGOING;
  40.             ptype->func(skb2, skb->dev, ptype, skb->dev);
  41.         }
  42.     }
  43.     rcu_read_unlock();
  44. }
说了这么多,我们看看没有队列的设备,

点击(此处)折叠或打开

  1. /* The device has no queue. Common case for software devices:
  2.      loopback, all the sorts of tunnels...

  3.      Really, it is unlikely that netif_tx_lock protection is necessary
  4.      here. (f.e. loopback and IP tunnels are clean ignoring statistics
  5.      counters.)
  6.      However, it is possible, that they rely on protection
  7.      made by us here.

  8.      Check this and shot the lock. It is not prone from deadlocks.
  9.      Either shot noqueue qdisc, it is even simpler 8)
  10.      */
  11.     if (dev->flags & IFF_UP) {
当然设备必须是开启的,up状态,典型的例子就是回环设备. 它没有队列的处理,而是直接把包发送出去,如果发送失败也不会重新发送,而是直接丢弃了.

这里我们只是大致说了下帧从ip层到驱动层的发送,没有涉及ip以上和网卡驱动的具体函数. 网卡驱动可以具体看驱动实例即可.关于流量控制这里只是简单说了一下调用流程,
具体分析还需要结合tc命令以及iptables详细分析. 到这里已经和帧的接收圆了起来.




     
相关文章
|
5月前
|
人工智能 Linux API
OpenClaw+本地模型无限Token实战:阿里云/本地部署+Ollama+百炼API配置指南
OpenClaw 配合本地小模型,可实现**完全免费、无限Token、数据不出本地、断网可用**的私人AI助手,彻底告别订阅费、额度限制与隐私泄露风险。本文完整覆盖:OpenClaw 2026阿里云/Windows11/MacOS/Linux全平台部署、Ollama快速搭建本地模型、阿里云百炼Coding Plan免费大模型配置、本地+云端双模型自动切换方案,所有命令可直接复制,全程无付费环节、无冗余表述,适合追求低成本、高隐私、高自由度的个人与轻量化团队。
2273 15
|
4月前
|
存储 安全 Java
Java 基础数据类型完全指南
本文详解Java 8种基本数据类型(byte/short/int/long/float/double/char/boolean)的取值范围、默认值、使用场景及注意事项,涵盖字面量规范、自动/强制类型转换、包装类与装箱拆箱机制,并强调金额计算须用BigDecimal、内存优化等最佳实践。
637 1
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1194 77
|
XML Java 数据库连接
十一、MyBatis的逆向工程
十一、MyBatis的逆向工程
498 6
十一、MyBatis的逆向工程
|
网络协议 算法 关系型数据库
MySQL8 中文参考(八十四)(5)
MySQL8 中文参考(八十四)
560 5
|
数据采集 自然语言处理 监控
《一文破解!自然语言处理论文实验复现秘籍》
在自然语言处理(NLP)领域,复现学术论文实验是将理论转化为实践的关键。前期需深入研读论文、收集数据与代码资源;搭建环境时要配置开发工具和预处理数据;模型训练中注重架构实现、优化器选择及训练监控;最后通过评估指标、结果对比与可视化分析确保复现成功。这一过程不仅验证研究成果,还提升技术能力,推动NLP领域的发展。
564 16
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
1532 8
|
SQL Java 数据库连接
求求你别乱脱敏了!MyBatis 插件 + 注解轻松实现数据脱敏,So easy~!
求求你别乱脱敏了!MyBatis 插件 + 注解轻松实现数据脱敏,So easy~!
1143 0
|
数据可视化
Echarts动态数据可视化学习(2)柱状图和折线图的动态数据更新
Echarts动态数据可视化学习(2)柱状图和折线图的动态数据更新
658 0
|
存储 算法 Java
趣味算法:滑动窗口算法的理解与应用
前言 在编程和数据结构中,滑动窗口算法是一种常见的解决问题的方法。它主要用于处理涉及连续或固定长度子数组、子序列或子字符串的问题。本文将深入探讨滑动窗口算法,包括其基本概念、应用场景、基本步骤以及具体的Java代码实践。
1104 0

热门文章

最新文章