資料包發送和接收有點類似,我們一般都用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處理的時候。