SO_INCOMING_CPU是Linux 5.10+只读套接字选项,用于获取最近一次收包所经CPU ID,不能设置或绑定中断;真正控制收包CPU需靠RSS、中断亲和性、RPS/RFS及SO_REUSEPORT等机制。

SO_INCOMING_CPU 是什么,它真能绑定网卡中断到指定 CPU 吗?
SO_INCOMING_CPU 是 Linux 5.10+ 引入的套接字选项,用于获取**内核在处理该 socket 收包时实际使用的 CPU ID**(即触发软中断 NET_RX 的 CPU),但它**不是设置项,不能主动“对齐”或“绑定”**。很多人误以为它是类似 SO_BINDTODEVICE 的配置接口,其实它只读——调用 getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, &len) 只能读出当前连接最近一次收包所经的 CPU,无法控制流向哪颗 CPU。
真正影响收包 CPU 分布的是底层机制:网卡多队列(RSS)、中断亲和性(/proc/irq/*/smp_affinity)、RPS/RFS 配置,以及应用层是否启用 SO_REUSEPORT 配合 CPU 轮转。想让连接“对齐”,得从这些地方下手,而不是指望 SO_INCOMING_CPU 去设值。
如何让某个 socket 收包稳定落在指定 CPU 上?
核心思路是:让网卡硬件中断、软中断、应用 poll 线程三者尽可能落在同一 CPU,减少跨核缓存同步开销。关键操作包括:
- 确认网卡支持 RSS,并启用多队列(如
ethtool -L eth0 combined 8) - 将网卡各 RX 队列的中断绑定到目标 CPU(例如 IRQ 123 对应队列 0:
echo 1 > /proc/irq/123/smp_affinity_list) - 关闭 RPS(
echo 0 > /sys/class/net/eth0/queues/rx-0/rps_cpus),避免内核软件层打乱 CPU 分配 - 应用层使用
SO_REUSEPORT创建多个监听 socket,每个线程bind()到相同端口但setaffinity绑定到不同 CPU,靠内核哈希把连接分发到对应线程 - 确保线程不被调度器迁移到其他 CPU(
pthread_setaffinity_np()+sched_setattr()设置 SCHED_FIFO 或 SCHED_RR)
此时再用 SO_INCOMING_CPU 查询,大概率会返回你期望的 CPU ID —— 它是结果,不是原因。
立即学习“C++免费学习笔记(深入)”;
为什么 getsockopt(..., SO_INCOMING_CPU, ...) 返回 -1 或 0?
常见错误现象:getsockopt 失败返回 -1 并设 errno = ENOPROTOOPT,或成功但读出值为 0。这通常因为:
- 内核版本低于 5.10 ——
SO_INCOMING_CPU尚未实现 - socket 尚未收到任何数据包(该值仅在第一次成功收包后才有效)
- 使用了非 TCP/UDP 的协议(如 RAW socket),目前仅 IPv4/v6 的流式与数据报 socket 支持
- 启用了
SO_ATTACH_REUSEPORT_CBPF或某些 eBPF 流控策略,干扰了 CPU 记录逻辑
验证方法:先用 nc -u 127.0.0.1 8080 发一个 UDP 包,再在服务端调用 getsockopt,看是否返回非零值。
SO_INCOMING_CPU 在高性能服务中怎么用才不踩坑?
它适合做运行时诊断,而非控制逻辑。典型误用是:收到值后立刻 sched_setaffinity 把线程绑过去 —— 这毫无意义,因为下个包可能来自另一队列,CPU 又变了。正确做法是:
- 启动时固定中断亲和性 + 应用线程绑定,形成静态拓扑
- 用
SO_INCOMING_CPU定期采样(比如每 1000 次 accept 后查一次),统计各 socket 的 CPU 分布,判断 RSS 哈希是否倾斜 - 配合
/proc/interrupts中断计数,交叉验证网卡队列负载是否均衡 - 若发现某 CPU 收包远超其他核,检查 RSS key 是否默认未随机化(需
ethtool -X eth0 hkey ...更新)
真正难的从来不是读一个 CPU 编号,而是让整个网络栈的中断、软中断、内存分配、线程调度全部协同在一条物理路径上 —— SO_INCOMING_CPU 只是一面镜子,照得见问题,照不出解法。


















