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
相关产品推荐
相关产品推荐

