找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1166

积分

1

好友

156

主题
发表于 前天 07:26 | 查看: 9| 回复: 0

TCP小队列(TSQ)机制的主要目标是限制单个TCP连接在排队规则(qdisc)层或设备层中积压的数据包数量。通过减少队列中的积压,可以有效降低网络传输的往返时间(RTT)和拥塞窗口(cwnd)的波动,这是缓解“缓冲膨胀”(bufferbloat)这一常见网络性能瓶颈问题的重要手段。

该机制的核心是限制套接字的sk->sk_wmem_alloc值,使其增长不超过一个预设的上限(默认约为128KB)。这意味着,在任何给定时刻,单个TCP套接字在队列中等待发送的数据总量被严格控制。

对于启用了TCP分段卸载(TSO)以提升吞吐量的场景,TSQ机制会将单个TSO大包的大小限制为此上限的一半。这一设计保证了最多只有两个TSO数据包处于“发送中”(in flight)状态,从而在有效控制队列深度的同时,维持了较高的带宽利用率。

此外,设置此限制还有一个有益的副作用:它会自动将通用分段卸载(GSO)的最大数据块大小从标准的65536字节降低到约20000字节。使用更小的GSO/TSO数据包有助于降低高优先级流量的排队延迟,提升整体网络性能

实现机制

为了实现上述控制,内核将TCP套接字原本的sock_wfree()释放回调函数替换为专用的tcp_wfree()。当队列中的skb(套接字缓冲区)被释放(调用skb_orphan())时,此函数会被触发,从而安排后续数据帧的发送。

由于skb的析构函数(destructor)在执行时可能已经持有了qdisc锁,无法直接重新启动发送流程,因此TSQ将实际的发送工作委托给一个tasklet(软中断下半部任务)。为了提升多核性能,系统为每个CPU核心都维护了一个独立的tasklet及其对应的待处理套接字队列。

效果与可调参数

在实际测试中(例如使用tg3网卡和标准pfifo_fast排队规则),TSQ机制效果显著。它能够在不降低网络标称带宽的前提下,有效抑制队列膨胀。例如,单个netperf会话不会再在qdisc中造成数MB的数据积压,避免了套接字自动调优占用过多系统资源。

该机制的行为可以通过一个新增的Linux系统参数进行调节:

  • /proc/sys/net/ipv4/tcp_limit_output_bytes:此参数定义了每个TCP套接字输出队列的字节数限制。

注意事项

skb_orphan()通常在网络传输完成时被调用,但有些网络设备驱动程序会在其start_xmit()函数中提前调用它。对于这类驱动,除非它们同时启用了字节队列限制(BQL),否则TSQ机制可能无法生效,单个TCP连接仍有可能会占满整个网卡的发送环缓冲。

核心代码摘要

以下补丁代码片段展示了TSQ在Linux内核中的关键实现逻辑,主要包括状态标志、tasklet初始化、专用的缓冲区释放函数tcp_wfree以及对发送路径的检查限制。

// 在tcp_sock结构中增加TSQ相关成员
struct tcp_sock {
    ...
    struct list_head tsq_node; // 用于挂载到TSQ任务列表
    unsigned long   tsq_flags;
    ...
};

// TSQ状态标志定义
enum tsq_flags {
    TSQ_THROTTLED, // 发送被限制
    TSQ_QUEUED,    // 套接字已加入任务队列
    TSQ_OWNED,     // 套接字被用户态锁定
};

// 每个CPU的TSQ任务结构
struct tsq_tasklet {
    struct tasklet_struct   tasklet;
    struct list_head    head; // TCP套接字队列
};
static DEFINE_PER_CPU(struct tsq_tasklet, tsq_tasklet);

// 初始化和定义全局限制参数
int sysctl_tcp_limit_output_bytes __read_mostly = 131072; // 默认限制 ~128KB
void __init tcp_tasklet_init(void) { ... }

// 核心的skb释放函数
void tcp_wfree(struct sk_buff *skb)
{
    struct sock *sk = skb->sk;
    struct tcp_sock *tp = tcp_sk(sk);

    if (test_and_clear_bit(TSQ_THROTTLED, &tp->tsq_flags) &&
        !test_and_set_bit(TSQ_QUEUED, &tp->tsq_flags)) {
        // 将套接字加入到当前CPU的TSQ任务列表,并调度tasklet
        ...
        tsq = &__get_cpu_var(tsq_tasklet);
        list_add(&tp->tsq_node, &tsq->head);
        tasklet_schedule(&tsq->tasklet);
        ...
    } else {
        sock_wfree(skb); // 回退到标准释放函数
    }
}

// 在发送路径中检查并设置限制
static bool tcp_write_xmit(struct sock *sk, ...)
{
    ...
    while ((skb = tcp_send_head(sk))) {
        ...
        // 如果已分配的内存超过限制,则设置THROTTLED标志并中断发送循环
        if (atomic_read(&sk->sk_wmem_alloc) >= sysctl_tcp_limit_output_bytes) {
            set_bit(TSQ_THROTTLED, &tp->tsq_flags);
            break;
        }
        ...
    }
}

文章来源参考:LWN.net - TCP Small Queues




上一篇:微服务分布式事务解决方案全景:Saga、TCC与Seata框架实战解析
下一篇:国产半导体设备技术突破:在HBM制造的TSV、刻蚀与键合环节实现全面布局
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 16:31 , Processed in 0.104836 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表