Linux TCP 单机优化
Posted dog250
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux TCP 单机优化相关的知识,希望对你有一定的参考价值。
TCP 的 send 函数 tcp_sendmsg/tcp_sendpage,要调用 lock_sock(sk)。
TCP 的 recv 函数 tcp_recvmsg,也要调用 lock_sock(sk):
void lock_sock_nested(struct sock *sk, int subclass)
might_sleep();
spin_lock_bh(&sk->sk_lock.slock);
if (sk->sk_lock.owned)
__lock_sock(sk);
sk->sk_lock.owned = 1;
spin_unlock(&sk->sk_lock.slock);
/*
* The sk_lock has mutex_lock() semantics here:
*/
mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_);
local_bh_enable();
__lock_sock 的核心即睡眠等锁。
这意味着 TCP socket 的 send 和 recv 互斥。睡眠等锁期间,CPU 时间不属于该 socket。
不光如此,软中断上下文 tcp_v4_rcv 处理 TCP 接收间,无论 send 还是 recv 均会在一个 spinlock 自旋,这意味着软中断 TCP 接收间,send 无法进行,recv 亦无法读取已按序齐整排入 receive queue 的数据。
这就是我常说的,TCP 称全双工传输,但 Linux TCP 实现却是半双工。我并不认为这是高尚的:
Linux TCP并不是全双工的
很多人怼我,说我根本不懂双工的概念,涉及到物理层,信道。我想他们并没有理解我在说什么。再解释也苍白,引用 iperf-2.0.14a 的 manual 来解释 socket 双工:
–full-duplex
run a full duplex test, i.e. traffic in both transmit and receive directions using the same socket
在我看来,高尚的做法是只保护读写共享的数据,细化锁粒度:
- sk_write_queue:发送队列,socket 进程上下文写入,tcp_write_xmit 进程上下文或软中断上下文摘除。
- tcp_rtx_queue:重传队列,socket 进程上下文或软中间上下文写入,tcp_clean_rtx_queue 软中断上下文摘除。
- sk_receive_queue:接收队列,socket 进程上下文 release_sock 或软中断上下文写入,socket 进程上下文摘除。
保护好这些数据结构,再小心翼翼处理一下其它共享元数据,send/recv/tcp_v4_rcv 即可并行。事情会高尚很多。
迄今为止,我想因为 Linux TCP 单机性能尚未触及瓶颈,拆锁重构工作量成本不小,却没有眼见的收益,社区没人闲着做这事。如今 100 Gbps 网卡越来越多,这些细节早晚会被盯上。
但还有一条路,若 DPDK 可实现好用的 TCP,内核协议栈可能就这么放着永远不动了:
- DPDK 提供高性能版本 TCP。
- Kernel 提供通用完备 TCP。
外说一句,曾经 UDP 也是类似 lock_sock,可能也是因为一把梭哈实现简单,后面随着 UDP 应用逐步推广,可能是 QUIC 加持也可能不是,UDP 的 lock_sock 消失了,锁粒度细化到保护特定的共享 queue,最终,连 queue 都拆成了两个,便于批量处理:
- Linux内核UDP收包为什么效率低?能做什么优化?
- Linux内核UDP收包为什么效率低?能做什么优化?
这也是历史发展的轨迹。
弄蟹!80块钱弄蟹,两大四小。
浙江温州皮鞋湿,下雨进水不会胖。
以上是关于Linux TCP 单机优化的主要内容,如果未能解决你的问题,请参考以下文章