Linux TC流控實(shí)現(xiàn)機(jī)制過(guò)程
一、TC核心架構(gòu)
Linux TC采用模塊化分層設(shè)計(jì),核心組件包括:
- Qdisc(排隊(duì)規(guī)則):流量調(diào)度的基本單元(如
pfifo_fast、htb) - Class(分類(lèi)):Qdisc內(nèi)部的子隊(duì)列(僅存在于分類(lèi)型Qdisc中)
- Filter(過(guò)濾器):將流量分類(lèi)到特定Class(如
u32、fwmark) - Policer(策略器):執(zhí)行速率限制(如
tbf) - Action(動(dòng)作):對(duì)數(shù)據(jù)包執(zhí)行操作(如
mirred重定向)
二、核心數(shù)據(jù)結(jié)構(gòu)
1.Qdisc結(jié)構(gòu)體(net/sched/sch_generic.c)
struct Qdisc {
int (*enqueue)(struct sk_buff *skb, struct Qdisc *sch); // 入隊(duì)操作
struct sk_buff* (*dequeue)(struct Qdisc *sch); // 出隊(duì)操作
struct Qdisc_ops *ops; // Qdisc操作函數(shù)集
struct netdev_queue *dev_queue; // 關(guān)聯(lián)的網(wǎng)絡(luò)設(shè)備隊(duì)列
};
2.Qdisc操作集(include/net/sch_generic.h)
struct Qdisc_ops {
struct Qdisc_ops *next;
const struct Qdisc_class_ops *cl_ops; // Class操作函數(shù)集
int (*enqueue)(struct sk_buff *, struct Qdisc *);
struct sk_buff * (*dequeue)(struct Qdisc *);
// ... 其他鉤子函數(shù)(init, destroy, reset等)
};
3.Filter結(jié)構(gòu)體(net/sched/cls_api.c)
struct tcf_proto {
__be16 protocol; // 匹配的協(xié)議(如ETH_P_IP)
struct tcf_proto_ops *ops; // Filter操作函數(shù)集
struct tcf_result result; // 分類(lèi)結(jié)果(指向Class)
};
三、關(guān)鍵處理流程
1.數(shù)據(jù)包入隊(duì)流程
graph TD
A[數(shù)據(jù)包到達(dá)] --> B{設(shè)備是否啟用TC?}
B -->|是| C[調(diào)用dev_queue_xmit()]
C --> D[執(zhí)行__dev_xmit_skb()]
D --> E[調(diào)用sch_direct_xmit() -> qdisc->enqueue()]
E --> F[Qdisc特定入隊(duì)邏輯]
F --> G[按調(diào)度算法緩存/丟棄]
2.數(shù)據(jù)包出隊(duì)調(diào)度
無(wú)分類(lèi)Qdisc(如pfifo):
static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *sch) {
struct sk_buff *skb = __qdisc_dequeue_head(&sch->q);
return skb;
}
分類(lèi)型Qdisc(如HTB):
struct sk_buff *htb_dequeue(struct Qdisc *sch) {
while ((skb = htb_do_dequeue(sch, prio, band)) != NULL) {
// 按類(lèi)別優(yōu)先級(jí)和令牌桶算法出隊(duì)
}
}
四、經(jīng)典Qdisc實(shí)現(xiàn)分析
1.HTB(Hierarchical Token Bucket)
核心機(jī)制:
- 令牌桶按層次分配帶寬
- 子類(lèi)可借用父類(lèi)空閑帶寬
關(guān)鍵數(shù)據(jù)結(jié)構(gòu):
struct htb_class {
struct Qdisc_class_common common;
struct psched_ratecfg rate; // 速率配置
struct psched_ratecfg ceil; // 上限配置
s64 tokens, ctokens; // 令牌計(jì)數(shù)
struct htb_class *parent; // 父類(lèi)指針
};
2.Netem(網(wǎng)絡(luò)模擬器)
實(shí)現(xiàn)延遲/丟包/亂序:
static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) {
if (loss_condition) { // 按概率丟包
kfree_skb(skb);
return NET_XMIT_SUCCESS;
}
if (delay_calculated) { // 計(jì)算延遲時(shí)間
tfifo = netem_skb_cb(skb);
tfifo->time_to_send = now + delay;
}
__qdisc_enqueue_tail(skb, &sch->q); // 加入延遲隊(duì)列
}
五、Filter與Classifier機(jī)制
1.U32過(guò)濾器示例
static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res) {
struct tc_u32_key *key = tp->data;
if (skb->len < key->off + 4) // 檢查偏移量是否有效
return -1;
if (*(u32*)(skb->data + key->off) == key->val) // 匹配關(guān)鍵值
res->classid = key->classid; // 設(shè)置分類(lèi)ID
}
2.eBPF集成(cls_bpf)
允許加載eBPF程序進(jìn)行高級(jí)分類(lèi):
static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res) {
struct cls_bpf_prog *prog = tp->data;
int ret = bpf_prog_run(prog->filter, skb); // 執(zhí)行eBPF程序
if (ret == TC_ACT_SHOT) return -1; // 丟棄包
res->classid = ret; // 設(shè)置分類(lèi)ID
}
六、TC配置接口(Netlink)
用戶(hù)空間工具:iproute2的tc命令
內(nèi)核處理路徑:
// net/sched/sch_api.c
static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n) {
struct net *net = sock_net(skb->sk);
struct tcmsg *tcm = nlmsg_data(n);
struct net_device *dev = __dev_get_by_index(net, tcm->tcm_ifindex);
// 解析并調(diào)用qdisc/class/filter操作函數(shù)
}
七、性能優(yōu)化機(jī)制
多隊(duì)列Qdisc (mq):
- 每個(gè)CPU核心一個(gè)隊(duì)列,減少鎖競(jìng)爭(zhēng)
FQ_Codel (Fair Queuing with Controlled Delay):
- 使用流哈希分離流量
- 基于延遲的ECN標(biāo)記
?
static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) {
struct fq_codel_flow *flow;
list_for_each_entry(flow, &q->new_flows, flowchain) {
skb = flow->head;
if (codel_time_after(skb->tstamp, now)) // 檢查是否需延遲
continue;
// ... 出隊(duì)邏輯
}
}
?八、調(diào)試與監(jiān)控
TC統(tǒng)計(jì)信息:
tc -s qdisc show dev eth0
內(nèi)核Tracepoint:
perf record -e 'net:net_dev_queue' -e 'net:net_dev_xmit'
九、代碼目錄結(jié)構(gòu)
net/sched/ ├── sch_generic.c // Qdisc基礎(chǔ)框架 ├── sch_htb.c // HTB實(shí)現(xiàn) ├── sch_netem.c // Netem實(shí)現(xiàn) ├── cls_api.c // Filter框架 ├── cls_u32.c // U32分類(lèi)器 ├── act_api.c // Action框架 └── act_mirred.c // 重定向Action
十、總結(jié)與挑戰(zhàn)
優(yōu)勢(shì):
- 靈活的分層流量控制
- 可擴(kuò)展的模塊化設(shè)計(jì)
挑戰(zhàn):
- 復(fù)雜配置導(dǎo)致學(xué)習(xí)曲線(xiàn)陡峭
- 單核處理瓶頸(部分Qdisc未充分并行化)
- 與XDP/BPF等新技術(shù)的整合
通過(guò)深入分析可見(jiàn),Linux TC通過(guò)抽象Qdisc/Class/Filter三層模型,實(shí)現(xiàn)了從簡(jiǎn)單FIFO到復(fù)雜分層調(diào)度的靈活控制,其代碼設(shè)計(jì)充分體現(xiàn)了UNIX的"組合小工具"哲學(xué)。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Linux文件服務(wù)器實(shí)戰(zhàn)詳解(系統(tǒng)用戶(hù))
這篇文章主要介紹了Linux文件服務(wù)器實(shí)戰(zhàn)詳解(系統(tǒng)用戶(hù)),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06
linux下多個(gè)硬盤(pán)劃分到同一掛載點(diǎn)問(wèn)題
在Linux系統(tǒng)中,將多個(gè)硬盤(pán)劃分到同一掛載點(diǎn)需要通過(guò)邏輯卷管理(LVM)來(lái)實(shí)現(xiàn),首先,需要將物理存儲(chǔ)設(shè)備(如硬盤(pán)分區(qū))創(chuàng)建為物理卷,然后,將這些物理卷組成邏輯卷組,接著,從邏輯卷組中劃分出邏輯卷,并進(jìn)行格式化和掛載2025-02-02
Jetson xavier nx的擴(kuò)容及安裝cuda經(jīng)驗(yàn)分享
這段描述主要圍繞圍繞述了Jetson設(shè)備內(nèi)存不足導(dǎo)致黑屏的解決方法,以及固態(tài)硬盤(pán)擴(kuò)容的具體步驟,包括進(jìn)入命令行刪除文件、固態(tài)硬盤(pán)安裝和配置等并還建議使用官方下載源以避免安裝過(guò)程中出現(xiàn)的問(wèn)題2026-06-06
CentOS虛擬機(jī)無(wú)法顯示本地IP的問(wèn)題及解決方案
文章介紹了在CentOS虛擬機(jī)中無(wú)法顯示本地IP的問(wèn)題,并提供了兩種解決方案:使用`network`服務(wù)禁用`NetworkManager`或反之,以及使用`NetworkManager`進(jìn)行配置,通過(guò)禁用沖突的服務(wù)或正確配置`NetworkManager`,可以解決IP顯示問(wèn)題2025-11-11
Linux(centos7)虛擬機(jī)沒(méi)有IP問(wèn)題及解決方案
文章介紹了在CentOS 7中配置虛擬機(jī)網(wǎng)絡(luò)并使用Xshell連接虛擬機(jī)的步驟,首先,檢查并配置網(wǎng)卡ens33的ONBOOT屬性為yes,然后重啟網(wǎng)絡(luò)服務(wù),接著,使用Xshell新建連接,輸入虛擬機(jī)的IP地址、端口號(hào)以及賬戶(hù)密碼,完成連接并可以在Xshell上執(zhí)行命令2025-11-11

