Linux TC流控实现机制过程

 更新时间:2025年09月30日 08:39:48   作者:Linux解析  
Linux TC通过模块化分层设计(Qdisc、Class、Filter、Policer、Action)实现灵活流量控制,支持从简单FIFO到复杂分层调度,其优势在于可扩展性,但存在配置复杂、单核瓶颈及新技术整合等挑战

一、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* (*dequeue)(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[Qdisc特定入队逻辑]
    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(网络模拟器)

实现延迟/丢包/乱序:

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_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); // 执行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

十、总结与挑战

优势

  • 灵活的分层流量控制
  • 可扩展的模块化设计

挑战

  • 复杂配置导致学习曲线陡峭
  • 单核处理瓶颈(部分Qdisc未充分并行化)
  • 与XDP/BPF等新技术的整合

通过深入分析可见,Linux TC通过抽象Qdisc/Class/Filter三层模型,实现了从简单FIFO到复杂分层调度的灵活控制,其代码设计充分体现了UNIX的"组合小工具"哲学。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • linux系统文件共享samba配置教程

    linux系统文件共享samba配置教程

    大家好,本篇文章主要讲的是linux系统文件共享samba配置教程,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • linux服务器之间传输文件的四种方式

    linux服务器之间传输文件的四种方式

    这篇文章主要介绍了linux服务器之间传输文件的四种方式的相关资料,需要的朋友可以参考下
    2016-04-04
  • Ubuntu配置固定IP的流程步骤

    Ubuntu配置固定IP的流程步骤

    在装服务器系统的时候,DHCP自动获取ip时,路由可能会重新分配ip,为避免产生影响,可以关闭DHCP将主机设置为静态ip,本文给大家介绍了Ubuntu配置固定IP的流程步骤,需要的朋友可以参考下
    2025-08-08
  • Linux文件服务器实战详解(系统用户)

    Linux文件服务器实战详解(系统用户)

    这篇文章主要介绍了Linux文件服务器实战详解(系统用户),非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2018-06-06
  • linux下多个硬盘划分到同一挂载点问题

    linux下多个硬盘划分到同一挂载点问题

    在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成逻辑卷组,接着,从逻辑卷组中划分出逻辑卷,并进行格式化和挂载
    2025-02-02
  • Jetson xavier nx的扩容及安装cuda经验分享

    Jetson xavier nx的扩容及安装cuda经验分享

    这段描述主要围绕围绕述了Jetson设备内存不足导致黑屏的解决方法,以及固态硬盘扩容的具体步骤,包括进入命令行删除文件、固态硬盘安装和配置等并还建议使用官方下载源以避免安装过程中出现的问题
    2026-06-06
  • CentOS虚拟机无法显示本地IP的问题及解决方案

    CentOS虚拟机无法显示本地IP的问题及解决方案

    文章介绍了在CentOS虚拟机中无法显示本地IP的问题,并提供了两种解决方案:使用`network`服务禁用`NetworkManager`或反之,以及使用`NetworkManager`进行配置,通过禁用冲突的服务或正确配置`NetworkManager`,可以解决IP显示问题
    2025-11-11
  • Linux(centos7)虚拟机没有IP问题及解决方案

    Linux(centos7)虚拟机没有IP问题及解决方案

    文章介绍了在CentOS 7中配置虚拟机网络并使用Xshell连接虚拟机的步骤,首先,检查并配置网卡ens33的ONBOOT属性为yes,然后重启网络服务,接着,使用Xshell新建连接,输入虚拟机的IP地址、端口号以及账户密码,完成连接并可以在Xshell上执行命令
    2025-11-11
  • apache 虚拟主机的配置方法

    apache 虚拟主机的配置方法

    apache 虚拟主机的配置方法,需要的朋友可以参考下。
    2010-04-04
  • 详解centos7配置本地yum源的方法

    详解centos7配置本地yum源的方法

    本篇文章主要介绍了详解centos7配置本地yum源的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧。
    2017-03-03

最新评论