开发者

Linux TC流控实现机制过程

开发者 https://www.devze.com 2025-09-30 10:02 出处:网络 作者: Linux解析
目录一、TC核心架构二、核心数据结构1.Qdisc结构体(net/sched/sch_generic.c)2.Qdisc操作集(include/net/sch_generic.h)3.Filter结构体(net/sched/cls_api.c)三、关键处理流程1.数据包入队流程2.数据包出队调度四、经
目录
  • 一、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采用模块化分层设计,核心组件包括:

            1. Qdisc(排队规则):流量调度的基本单元(如pfifo_fasthtb
            2. Class(分类):Qdisc内部的子队列(仅存在于分类型Qdisc中)
            3. Filter(过滤器):将流量分类到特定Class(如u32fwmark
            4. Policer(策略器):执行速率限制(如tbf
            5. 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)

            用户空间工具iproute2tc命令

            内核处理路径

            // 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)。

            0

            精彩评论

            暂无评论...
            验证码 换一张
            取 消

            关注公众号