协议栈数据发送

传输层的数据发送

传输层的数据发送的触发

   1、由用户进程下一次send系统调用

   2、协议栈收到数据反馈后执行数据的发送

tcp_write_xmit()
{
  while ((skb = tcp_send_head(sk))) 
  {
     tcp_event_new_data_sent(sk, skb);//重新获取首部
  }

}


数据发送经过ip层路由和邻居协议后

发送队列关联了Qdisc
net/core/Dev.c
int dev_queue_xmit(struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;//ip层路由的时候,会计算得到包的出口设备
	struct netdev_queue *txq;
	struct Qdisc *q;
	txq = dev_pick_tx(dev, skb);
	q = rcu_dereference(txq->qdisc);
	if (q->enqueue) {
		rc = __dev_xmit_skb(skb, q, dev, txq);
		goto out;
	}
}
多队列网卡需要选择从哪个队列出去
net/core/Dev.c
static struct netdev_queue *dev_pick_tx(struct net_device *dev,struct sk_buff *skb)
{
	const struct net_device_ops *ops = dev->netdev_ops;
	u16 queue_index = 0;
	if (ops->ndo_select_queue)
		queue_index = ops->ndo_select_queue(dev, skb);
	else if (dev->real_num_tx_queues > 1)
		queue_index = skb_tx_hash(dev, skb);
	skb_set_queue_mapping(skb, queue_index);
	return netdev_get_tx_queue(dev, queue_index);
}
将包放入到Qdisc中
net/core/dev.c
static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q, struct net_device *dev,struct netdev_queue *txq)
{
        .....
	rc = qdisc_enqueue_root(skb, q);
	qdisc_run(q);
       .....
	return rc;
}
static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
  return sch->enqueue(skb, sch);
}


static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch)
{
   qdisc_skb_cb(skb)->pkt_len = skb->len;
   return qdisc_enqueue(skb, sch) & NET_XMIT_MASK;
}
启动发送的软中断
net/sched/sch_generic.c
void __qdisc_run(struct Qdisc *q)
{
	unsigned long start_time = jiffies;
	while (qdisc_restart(q)) {
		if (need_resched() || jiffies != start_time) {
			__netif_schedule(q);
			break;
		}
	}
	clear_bit(__QDISC_STATE_RUNNING, &q->state);
}
net/core/dev.c
static inline void __netif_reschedule(struct Qdisc *q)
{
	struct softnet_data *sd;
	unsigned long flags;
	local_irq_save(flags);
	sd = &__get_cpu_var(softnet_data);
	q->next_sched = sd->output_queue;
	sd->output_queue = q;
	raise_softirq_irqoff(NET_TX_SOFTIRQ);
	local_irq_restore(flags);
}
net/core/dev.c
void __netif_schedule(struct Qdisc *q)
{
	if (!test_and_set_bit(__QDISC_STATE_SCHED, &q->state))
		__netif_reschedule(q);
}

数据即使触发了协议栈的发送,也不会立即出网卡,还要经过Qdisc的流量调节,Qdisc的流量调节是通过软中断来控制

这决定了数据的发送又是一个异步的过程了

用户态=>传输层发送队列=>网卡的多队列或者Qdisc队列

3个环节都是异步执行的

第一个环节是要用户态切换到内核态

第二个环节都是在内核态,只不过后续的数据发送在中断的上下文中处理


展开阅读全文

没有更多推荐了,返回首页