You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux用户态TUN接口write()慢的优化及1Gbps+隧道实现问询

隧道性能优化问题及分析

埋点实现代码

我为代码添加了如下埋点:

state.worker_stats.counters[TUN_TX]++;
state_switch_activity(KERN_WRITE_TUN);
ssize_t wrote = write(peer->tun_fd, plaintext, plaintext_len);
state_switch_activity(USER_UDP_TO_TUN);

其中计时核心函数实现:

static uint64_t nanonow() {
    struct timespec t;
    int err = clock_gettime(CLOCK_MONOTONIC, &t);
    if (err) {
        perror(__FILE__);
        exit(errno);
    }
    return t.tv_nsec + 1000000000 * t.tv_sec;
}

enum state_activity state_switch_activity(enum state_activity new_activity) {
    uint64_t now = nanonow();
    struct state_worker_stats *ws = &state.worker_stats; // abbreviate "state.worker_stats."

    enum state_activity old_activity = ws->current_activity;
    ws->current_activity = new_activity;
    uint64_t duration = now - ws->last_change;
    ws->last_change = now;
    ws->times[old_activity] += duration;

    return old_activity;
}

各阶段耗时统计

通过埋点得到每数据包的耗时统计:

outbound:
      KERN_READ_TUN per TUN_RX:      3.199167 µs
    USER_TUN_TO_UDP per TUN_RX:      4.038177 µs
       USER_ENCRYPT per UDP_TX_UNIQ: 7.955924 µs
      KERN_SEND_UDP per UDP_TX:      15.789421 µs
    USER_TX_HISTORY per UDP_TX_UNIQ: 0.603579 µs

  incoming:
      KERN_RECV_UDP per UDP_RX:      6.712680 µs
    USER_UDP_TO_TUN per UDP_RX:      5.095780 µs
    USER_RX_HISTORY per UDP_RX:      1.087274 µs
       USER_DECRYPT per UDP_RX_UNIQ: 11.485002 µs
     KERN_WRITE_TUN per TUN_TX:      20.241663 µs

注:我已将用户态和内核/系统统计总量与getrusage()结果做一致性校验,数据可信度高。

从统计结果看,入站数据包中写入tun0的耗时最长(单包20.24µs),UDP发送速度也偏慢。


技术问题及解答

1. 如何提升write()和sendmsg()的速度?

当前单包1500字节、耗时25µs的情况下,理论上限仅480Mbps,可从以下几个方向优化:

  • 批量IO操作:避免单包调用write()/sendmsg(),攒够多个数据包后用writev()/sendmmsg()一次性发送,减少系统调用次数。比如每次攒10个1500字节包,系统调用次数降为1/10,单包平均耗时会显著降低。
  • 调整套接字选项:
    • 增大套接字发送缓冲区(SO_SNDBUF),避免因缓冲区满导致的阻塞;
    • 开启MSG_NOSIGNAL避免发送错误时产生信号;
  • 使用异步IO:用io_uring替代传统的阻塞/非阻塞IO,把IO操作提交到内核队列,批量处理完成事件,大幅降低系统调用开销。
  • 绑定CPU核心:把处理IO的线程绑定到固定CPU核心,减少上下文切换开销,同时避免跨核心缓存失效。

2. Linux用户态下能否实现1Gbps+的隧道?

可以实现。只要优化到位,用户态隧道完全能突破1Gbps瓶颈,不少成熟的开源隧道工具在硬件达标时能轻松跑满10Gbps。

3. 具体如何实现?

要达到1Gbps+的性能,需要从IO路径、内存管理、CPU优化三个维度入手:

  • IO路径优化:
    • 用io_uring替代传统的read()/write()/sendmsg(),实现低拷贝的批量IO处理;
    • 对TUN设备开启IFF_NO_PI(无协议信息),减少内核和用户态之间的数据拷贝;
    • 用sendmmsg()/recvmmsg()批量处理UDP包,降低系统调用频率。
  • 内存管理优化:
    • 采用内存池机制,预先分配固定大小的数据包缓冲区,避免频繁的malloc()/free()开销;
    • 使用大页内存(HugeTLB),减少TLB缓存失效,提升内存访问速度;
    • 尽量让数据缓冲区对齐到CPU缓存行,避免伪共享问题。
  • CPU优化:
    • 把隧道的读写、加密解密等任务拆分到独立线程,并绑定到不同CPU核心,实现并行处理;
    • 优化加密解密逻辑,使用硬件加速指令(如AES-NI),或采用更高效的加密算法;
  • 系统参数调优:
    • 调整内核网络参数,如增大net.core.rmem_max/net.core.wmem_max,优化TCP/UDP缓冲区;
    • 调整TUN设备的队列长度,避免数据包堆积。

内容的提问来源于stack exchange,提问作者fadedbee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 13:43:24