3、操作系统调优:内核旁路、中断亲和性、CPU隔离

操作系统调优,说白了就是让Linux内核别来添乱。

我刚开始做量化交易系统那会儿,总觉得代码写得够快就行了。后来发现,辛辛苦苦优化到微秒级的逻辑,被操作系统一个上下文切换就给干到毫秒级去了。嗯,那时候我才意识到——操作系统本身,就是最大的延迟来源

3.1 内核旁路:绕过那个“中间商”

先说说内核旁路。你想想看,传统网络收发包要走什么路径?

  • 网卡收到数据 → 中断通知内核 → 内核协议栈处理 → 拷贝到用户态
  • 这一套下来,少说几十微秒没了

对于高频交易来说,这简直是灾难。我个人习惯的做法是:直接绕过内核,让用户态程序接管网卡

核心思路:使用DPDK、Solarflare的OpenOnload、或Mellanox的VMA这类技术,把网卡的控制权从内核手里抢过来。

举个例子,DPDK的工作方式是这样的:

// 传统方式:socket recv
recv(sockfd, buf, len, 0);  // 内核参与,延迟高

// DPDK方式:轮询网卡
struct rte_mbuf *mbuf;
while (1) {
    mbuf = rte_eth_rx_burst(port, queue, &pkts, 1);
    if (mbuf) {
        // 直接处理,无内核介入
        process_packet(pkts);
    }
}

我在项目中遇到过一件事:某次做行情网关优化,用传统socket方式,延迟在50微秒左右。换成DPDK后,直接压到了5微秒以内。10倍的差距,就只是绕过了内核而已。

避坑指南:我曾经以为DPDK是万能的,后来发现它也有代价——你得自己实现TCP/IP协议栈。如果只是做UDP行情接收,问题不大;但要做TCP交易,建议用OpenOnload这种半内核旁路方案。

3.2 中断亲和性:让CPU专心干一件事

中断亲和性,说白了就是告诉操作系统:这个网卡的中断,只许发给这个CPU核

为什么要这么做?

  • 默认情况下,中断是随机分配到各个CPU的
  • 这会导致CPU缓存频繁失效(cache miss)
  • 每次中断换到不同核,L1/L2缓存都得重新加载

我建议的做法是:

  1. 先查看网卡中断的IRQ号
  2. 然后绑定到指定的CPU核上
# 查看网卡中断
cat /proc/interrupts | grep eth0

# 绑定中断到CPU 0-3
echo 0f > /proc/irq/<IRQ_NUM>/smp_affinity

这里有个细节:smp_affinity用的是位图。比如0f表示CPU 0-3,01表示只绑CPU 0。我个人习惯把网卡中断绑到物理核上,避开超线程核。

注意:我曾经踩过一个坑——把中断和业务线程绑到了同一个核上。结果中断一来,业务线程被抢占了,延迟反而更高。正确的做法是:中断绑在核A,业务线程绑在核B,各干各的。

3.3 CPU隔离:给交易线程一个“专属房间”

CPU隔离,就是让某些CPU核完全脱离操作系统的调度。普通进程别想用这些核,只有你指定的线程才能跑。

为什么要隔离?

  • Linux默认的调度器是公平的
  • 它会时不时把你的交易线程换出去,让别的进程跑一会儿
  • 这一换,可能就是几十微秒的延迟抖动

我常用的配置方法:

# 在grub配置中添加isolcpus参数
GRUB_CMDLINE_LINUX="isolcpus=2,3"

# 更新grub后重启
update-grub
reboot

重启后,CPU 2和3就变成了“孤岛”。普通进程不会用它们。然后你用taskset把交易线程绑上去:

# 把进程绑定到CPU 2
taskset -c 2 ./trading_engine

进阶技巧:光隔离还不够,我建议配合rcu_nocbsnohz_full一起用。这两个参数能让隔离核彻底摆脱内核的定时器中断和RCU回调。配置如下:

GRUB_CMDLINE_LINUX="isolcpus=2,3 nohz_full=2,3 rcu_nocbs=2,3"

我记得有一次帮朋友调一个期货交易系统。他们用了8核机器,但延迟总是有20-30微秒的抖动。我一看,所有线程都在所有核上乱跑。后来我帮他们做了CPU隔离:2个核跑行情,2个核跑交易,1个核跑风控,剩下3个留给系统。抖动直接降到了5微秒以内。

小建议:隔离核的数量不要贪多。留1-2个核给操作系统,否则系统本身会变得不稳定。我一般留2个核给内核和日常任务。

3.4 知识体系总览

下面这张图,是我自己总结的操作系统调优核心逻辑:

操作系统调优三大核心方法 内核旁路 绕过内核协议栈 用户态直接接管网卡 延迟从50μs → 5μs DPDK / OpenOnload / VMA 适合UDP行情,TCP需谨慎 中断亲和性 绑定网卡中断到指定CPU 减少cache miss 避免中断抢占业务线程 smp_affinity位图配置 中断核 ≠ 业务核 CPU隔离 isolcpus隔离指定核 taskset绑定交易线程 消除调度抖动 配合nohz_full + rcu_nocbs 留2个核给系统 三者协同工作流程 ① 内核旁路 → 数据直达用户态,不经过内核 ② 中断亲和性 → 网卡中断固定到专用核 ③ CPU隔离 → 交易线程独占核,不受干扰 最终效果:延迟从毫秒级 → 微秒级,抖动消除

这三招用好了,你的交易系统就能在操作系统层面站稳脚跟。下一层,我们就要深入到CPU和内存的硬件调优了。

一句话总结:内核旁路解决“谁处理数据”的问题,中断亲和性解决“谁通知你”的问题,CPU隔离解决“谁抢你CPU”的问题。三管齐下,延迟自然就降下来了。


专注资料整理