Linux TC流控实现机制过程
目录
- 一、TC核心架构
- 二、核心数据结构
- 1.Qdisc结构体(net/sched/sch_generic.c)
- 2.Qdisc操作集(include/net/sch_generic.h)
- 3.Filter结构体(net/sched/cls_api.c)
- 三、关键处理流程
- 1.数据包入队流程
- 2.数据包出队调度
- 四、经典Qdisc实现分析
- 1.HTB(Hierarchical Token Bucket)
- 2.Netem(网络模拟器)
- 五、Filter与Classifier机制
- 1.U32过滤器示例
- 2.eBPF集成(cls_bpf)
- 六、TC配置接口(Netlink)
- 七、性能优化机制
- 八、调试与监控
- 九、代码目录结构
- 十、总结与挑战
一、TC核心架构
linux TC采用模块化分层设计,核心组件包括:
- Qdisc(排队规则):流量调度的基本单元(如
pfifo_fast
、htb
) - Class(分类):Qdisc内部的子队列(仅存在于分类型Qdisc中)
- Filter(过滤器):将流量分类到特定Class(如
u32
、fwmark
) - Policer(策略器):执行速率限制(如
tbf
) - Action(动作):对数据包执行操作(如
mirred
重定向)
二、核心数据结构
1.Qdisc结构体(net/sched/sch_generic.c)
struct Qdisc { int (*enqueue)(struct sk_buff *skb, struct Qdisc *sch); // 入队操作 struct sk_buff* (*d编程客栈equeue)(struct Qdisc *sch); // 出队操作 struct Qdisc_ops *ops; // Qdisc操作函数集 struct netdev_queue *dev_queue; // 关联的网络设备队列 };
2.Qdisc操作集(include/net/sch_generic.h)
struct Qdisc_ops { struct Qdisc_ops *next; const struct Qdisc_class_ops *cl_ops; // Class操作函数集 int (*enqueue)(struct sk_buff *, struct Qdisc *); struct sk_buff * (*dequeue)(struct Qdisc *); // ... 其他钩子函数(init, destroy, reset等) };
3.Filter结构体(net/sched/cls_api.c)
struct tcf_proto { __be16 protocol; // 匹配的协议(如ETH_P_IP) struct tcf_proto_ops *ops; // Filter操作函数集 struct tcf_result result; // 分类结果(指向Class) };
三、关键处理流程
1.数据包入队流程
graph TD A[数据包到达] --> B{设备是否启用TC?} B -->|是| C[调用dev_queue_xmit()] C --> D[执行__dev_xmit_skb()] D --> E[调用sch_direct_xmit() -> qdisc->enqueue()] E --> F[Qphpdisc特定入队逻辑] F --> G[按调度算法缓存/丢弃]
2.数据包出队调度
无分类Qdisc(如pfifo
):
static struct sk_buff *pfifo_fast_dequeue(struct Qdisc *sch) { struct sk_buff *skb = __qdisc_dequeue_head(&sch->q); return skb; }
分类型Qdisc(如HTB
):
struct sk_buff *htb_dequeue(struct Qdisc *sch) { while ((skb = htb_do_dequeue(sch, prio, band)) != NULL) { // 按类别优先级和令牌桶算法出队 } }
四、经典Qdisc实现分析
1.HTB(Hierarchical Token Bucket)
核心机制:
- 令牌桶按层次分配带宽
- 子类可借用父类空闲带宽
关键数据结构:
struct htb_class { struct Qdisc_class_common common; struct psched_ratecfg rate; // 速率配置 struct psched_ratecfg ceil; // 上限配置 s64 tokens, ctokens; // 令牌计数 struct htb_class *parent; // 父类指针 };
2.Netem(网络模拟器)
实现延迟python/丢包/乱序:
static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) { if (loss_condition) { // 按概率丢包 kfree_skb(skb); return NET_XMIT_SUCCESS; } if (delay_calculated) { // 计算延迟时间 tfifo = netem_skb_cb(skb); tfifo->time_to_send = now + delay; } __qdisc_enqueue_tail(skb, &sch->q); // 加入延迟队列 }
五、Filter与Classifier机制
1.U32过滤器示例
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) // 匹配关键值 res->classid = key->classid; // 设置分类ID }
2.eBPF集成(cls_bpf)
允许加载eBPF程序进行高级分类:
static int cls_bpf_classifyjs(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); // 执行eBPF程序 if (ret == TC_ACT_SHOT) return -1; // 丢弃包 res->classid = ret; // 设置分类ID }
六、TC配置接口(Netlink)
用户空间工具:iproute2
的tc
命令
内核处理路径:
// 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); // 解析并调用qdisc/class/filter操作函数 }
七、性能优化机制
多队列Qdisc (mq
):
- 每个CPU核心一个队列,减少锁竞争
FQ_Codel (Fair Queuing with Controlled Delay):
- 使用流哈希分离流量
- 基于延迟的ECN标记
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; // ... 出队逻辑 } }
八、调试与监控
TC统计信息:
tc -s qdisc show dev eth0
内核Tracepoint:
perf record -e 'net:net_dev_queue' -e 'net:net_dev_xmit'
九、代码目录结构
net/sched/ ├── sch_generic.c // Qdisc基础框架 ├── sch_htb.c // HTB实现 ├── sch_netem.c // Netem实现 ├── cls_api.c // Filter框架 ├── cls_u32.c // U32分类器 ├── act_api.c // Action框架 └── act_mirred.c // 重定向Action
十、总结与挑战
优势:
- 灵活的http://www.devze.com分层流量控制
- 可扩展的模块化设计
挑战:
- 复杂配置导致学习曲线陡峭
- 单核处理瓶颈(部分Qdisc未充分并行化)
- 与XDP/BPF等新技术的整合
通过深入分析可见,Linux TC通过抽象Qdisc/Class/Filter三层模型,实现了从简单FIFO到复杂分层调度的灵活控制,其代码设计充分体现了Unix的"组合小工具"哲学。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论