传输层的数据发送
传输层的数据发送的触发
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个环节都是异步执行的
第一个环节是要用户态切换到内核态
第二个环节都是在内核态,只不过后续的数据发送在中断的上下文中处理