数据包发送和接收有点类似,我们一般都用int dev_queue_xmit(struct sk_buff *skb)来发送数据包,例如我们自己构造完整的数据包,最后调用真正的物理发送函数。
但是我们也知道发送也是有软中断处理的,
open_softirq(NET_TX_SOFTIRQ,net_tx_action);
什么时候会触发这个软中断成为关键呢?其实这个软中断并不是必须的,只是因为传输过程中会出现一些问题,需要后续的软中断来处理。例如我比较关注的TC部分。
如果配置了TC,则调用__dev_xmit_skb,首先会对数据包进行enqueue,接着__qdisc_run进行发送,同时判断是否应该调度了,如果是,则__netif_schedule(q);这里就是进行软中断触发的条件。
进一步说明,其实还是有其他地方进行软中断触发,但是太过于细节,当前没有进一步研究。
排队准则接口
数据包发送过程中最重要的一个部分之一就是TC,也就是排队准则。
struct Qdisc_ops {
structQdisc_ops *next;
conststruct Qdisc_class_ops *cl_ops;
char id[IFNAMSIZ];
int priv_size;
int (*enqueue)(structsk_buff *, struct Qdisc *);
structsk_buff * (*dequeue)(struct Qdisc *);
structsk_buff * (*peek)(struct Qdisc *);
unsignedint (*drop)(struct Qdisc*);
int (*init)(struct Qdisc *,struct nlattr *arg);
void (*reset)(struct Qdisc *);
void (*destroy)(struct Qdisc *);
int (*change)(struct Qdisc*, struct nlattr *arg);
void (*attach)(struct Qdisc *);
int (*dump)(struct Qdisc *,struct sk_buff *);
int (*dump_stats)(structQdisc *, struct gnet_dump *);
structmodule *owner;
};
Enqueue将数据包进队列
Dequeue将数据包出队列
Requeue将数据包重新进队列
void __qdisc_run(struct Qdisc *q)
{
unsignedlong start_time = jiffies;
while(qdisc_restart(q)) {
/*
* Postpone processing if
* 1. another process needs the CPU;
* 2. we've been doing it for too long.
*/
if(need_resched() || jiffies != start_time) {
__netif_schedule(q);
break;
}
}
qdisc_run_end(q);
}
qdisc_restart负责发送数据包,后续将专门研究一下TC实现。
总结一下dev_queue_xmit,一是通过QoS层将调用hard_start_xmit发送数据,或者是直接调用hard_start_xmit来发送。
到现在我们只需要知道上层协议最后调用dev_queue_xmit来发送数据,有可能是直接发送出去,也有可能是经过软中断后发送,例如有QoS处理的时候。