本文目录导读:

- 理解:为什么不能直接绑定?
- 方法一:使用
taskset绑定整个进程及其 TCP 连接 - 方法二:使用
cgroup cpuset进行更细粒度的控制 - 方法三:禁用特定 CPU 上的 tcp_autocorking(通过 sysctl)
- 方法四:利用 SO_INCOMING_CPU 和 epoll 进行 socket-CPU 固定
- 方法五:使用 RPS(Receive Packet Steering)间接影响发送路径
- 最实用方案
在 Linux 网络栈中,tcp_autocorking 是一个内核特性(从 3.14 版本引入),它自动延迟小数据包的发送,以合并为更大的 TCP 段,从而减少网络开销。这个功能本身是全局内核行为,没有直接的 sysctl 或 per-socket 选项来将它绑定到特定 CPU。
如果你希望控制这个功能在哪些 CPU 上生效,或者想让相关进程/连接固定到某个 CPU,有几种间接方法可以实现类似“CPU 绑定”的效果。
理解:为什么不能直接绑定?
tcp_autocorking 是 TCP/IP 协议栈在处理 tcp_sendmsg() 时的逻辑决策,它发生在发起系统调用的进程上下文中,这个功能的实际执行 CPU 就是进程当前运行的 CPU。
所谓的“绑定”其实就是:
- 让所有使用 TCP 连接的进程固定在你希望的 CPU 上
- 或者,控制该功能的全局开关在某些 CPU 上被禁用
使用 taskset 绑定整个进程及其 TCP 连接
这是最直接的方法:将运行网络服务的进程绑定到固定 CPU。
# 将 nginx 绑定到 CPU 0 和 1 taskset -cp 0,1 $(pgrep nginx) # 或直接启动时绑定 taskset -c 0,1 /usr/sbin/nginx
效果:
- 该进程的所有
sendmsg系统调用都在指定 CPU 上执行 tcp_autocorking决策也在该 CPU 上发生- 同时也会影响 CPU 缓存局部性,通常对性能有利
使用 cgroup cpuset 进行更细粒度的控制
如果你需要为不同进程组分配不同 CPU,cgroup 是更好的选择。
# 创建 cpuset 组 mkdir /sys/fs/cgroup/cpuset/myapp echo "0-2" > /sys/fs/cgroup/cpuset/myapp/cpuset.cpus echo "0" > /sys/fs/cgroup/cpuset/myapp/cpuset.mems # 将进程加入 echo $PID > /sys/fs/cgroup/cpuset/myapp/cgroup.procs
这样所有相关 socket 操作都被限制在 CPU 0-2 上。
禁用特定 CPU 上的 tcp_autocorking(通过 sysctl)
tcp_autocorking 是全局开关(net.ipv4.tcp_autocorking = 1),你不能按 CPU 禁用,但可以用一个技巧:
- 在非目标 CPU 上将进程绑定(通过 irqbalance 或隔离 CPU)
- 然后在整个系统层面禁用它,仅让绑定的进程启用它
但这通常不推荐,因为失去了自动 cork 的优势。
更常见的是:
# 全局启用(默认) sysctl -w net.ipv4.tcp_autocorking=1 # 若想禁用某些连接的 cork,在 socket 层面设置 TCP_NODELAY # setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
利用 SO_INCOMING_CPU 和 epoll 进行 socket-CPU 固定
对于监听 socket 接受的连接,你可以让其保持在与接受它的同一个 CPU 上:
// 获取 socket 当前绑定的 CPU int cpu = 0; socklen_t len = sizeof(cpu); getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, &len); // 将线程绑定到该 CPU(使用 pthread_setaffinity_np 或 sched_setaffinity) cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
这样,每个连接的 tcp_autocorking 处理会固定在接受它的 CPU 上。
使用 RPS(Receive Packet Steering)间接影响发送路径
虽然 RPS 影响的是接收路径,但它通过将软中断分散到不同 CPU,间接影响了后续发送的 CPU 选择:
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus # 这里的 f 表示使用 CPU 0-3
但这对发送路径是间接的,不能精确控制 tcp_autocorking 的 CPU。
最实用方案
| 需求 | 做法 |
|---|---|
| 让某个服务(如数据库)的 TCP 发送固定到 CPU 2 | taskset -c 2 service_program |
| 让不同连接固定到不同 CPU | 用 SO_INCOMING_CPU + pthread_setaffinity_np |
| 完全禁用 tcp_autocorking | sysctl net.ipv4.tcp_autocorking=0 |
| 只想让某些 socket 不 cork | 设置 TCP_NODELAY socket 选项 |
tcp_autocorking 本身没有 CPU 绑定机制,你只能通过绑定进程或绑定 socket 到特定 CPU 来间接实现它的 CPU 亲和性。
标签: CPU绑定