3. 操作系统调优:Linux Kernel 编译选项与硬件资源隔离

做高频交易,操作系统层面的调优,说白了就是给交易程序「铺路」。

我见过太多团队,策略写得漂亮,代码也优化了,结果跑在默认的Linux上,延迟忽高忽低。为什么?因为操作系统压根没为你的程序让路。它忙着调度其他进程、处理中断、管理内存,你的交易指令只能排队等着。

这一章,我们就来聊聊怎么让Linux彻底「听话」。

核心目标:将操作系统对交易进程的干扰降到最低,实现可预测的微秒级延迟。

3.1 Kernel 编译选项:RT-Preempt 与 Tickless

先说说内核。默认的Linux内核是「公平」的,它对所有进程一视同仁。但高频交易不需要公平,需要的是「特权」。

RT-Preempt(实时抢占)

这个补丁把Linux变成了一个硬实时操作系统。我刚开始接触时也觉得夸张,一个通用操作系统能实时?后来在芝加哥的机房实测,效果确实明显。

它的原理很简单:允许高优先级进程随时抢占低优先级进程。哪怕内核正在处理系统调用,你的交易线程也能插队。

# 编译内核时启用 RT-Preempt
make menuconfig
# 进入 General setup -> Preemption Model
# 选择: Fully Preemptible Kernel (RT)

我的经验:启用RT后,中断响应时间从几十微秒降到了个位数微秒。但要注意,这会牺牲一点吞吐量。做高频交易,我们不在乎吞吐量,在乎的是延迟上限。

Tickless(无滴答)

默认内核每1毫秒会触发一次定时器中断,叫「tick」。这个中断会打断CPU,哪怕你的交易线程正在执行关键路径。

Tickless内核去掉了这个固定中断。CPU只在有任务时才被唤醒。嗯,这里要注意,完全无滴答(CONFIG_NO_HZ_FULL)需要配合CPU隔离使用。

# 内核配置选项
CONFIG_NO_HZ_FULL=y
# 指定哪些CPU进入完全无滴答模式
nohz_full=2-7

避坑指南:我曾经在某个项目里只开了nohz_full,没做CPU隔离。结果发现tick中断还是偶尔出现。后来才明白,nohz_full必须和isolcpus配合使用,否则白搭。

3.2 CPU隔离:isolcpus

CPU隔离,就是把某些CPU核心从系统的调度器中「摘」出来。系统不会在这些核心上运行其他进程,只有你指定的进程才能用。

我个人习惯把物理核心留给交易程序,逻辑核心(超线程)留给系统和其他服务。

# 在GRUB启动参数中添加
isolcpus=2,3,4,5,6,7

# 然后使用taskset绑定进程
taskset -c 2,3 ./trading_engine

为什么这么做?你想想看,如果系统突然要处理一个网络中断,而你的交易线程正好在那个核心上,延迟就会飙升。隔离后,系统中断会落在其他核心上,交易核心干干净净。

关键点:隔离的核心数要留够。我一般留2个核心给系统(0和1),其余全部隔离给交易程序。具体看你的CPU拓扑。

3.3 中断亲和性:IRQ Affinity

网络数据包到达网卡后,会触发硬件中断。这个中断默认由哪个CPU处理?系统随机分配。这对高频交易是灾难。

中断亲和性,就是手动指定中断由哪个CPU处理。我建议把所有网络中断都绑定到非交易核心上。

# 查看网卡中断号
cat /proc/interrupts | grep eth0

# 假设中断号是 78,将其绑定到 CPU 0
echo 1 > /proc/irq/78/smp_affinity
# 1 是位掩码,表示 CPU 0
# 如果绑定到 CPU 0 和 1,掩码是 3

这里有个小技巧:用十六进制掩码。CPU 0 是 0x01,CPU 1 是 0x02,CPU 2 是 0x04,以此类推。

我的习惯:把网卡中断绑到CPU 0,把存储中断绑到CPU 1。交易程序独占CPU 2-7。这样中断处理不会污染交易核心的缓存。

3.4 内存大页:HugePages

内存管理是另一个延迟来源。默认的4KB内存页,意味着TLB(页表缓存)很容易被撑爆。一旦TLB miss,CPU就要去查页表,那叫一个慢。

大页(HugePages)把页大小提升到2MB甚至1GB。TLB能覆盖的内存范围瞬间扩大几十倍。说白了,就是让CPU少跑腿。

# 分配 1024 个 2MB 大页
echo 1024 > /proc/sys/vm/nr_hugepages

# 查看是否成功
cat /proc/meminfo | grep HugePages
# HugePages_Total: 1024
# HugePages_Free: 1024

程序里怎么用?用mmap加上MAP_HUGETLB标志。

#include <sys/mman.h>

void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
                  -1, 0);
if (addr == MAP_FAILED) {
    perror("mmap hugepages failed");
    exit(1);
}

曾经踩过的坑:大页分配后,系统不会自动回收。如果程序崩溃退出,大页内存会一直占用。我后来写了个监控脚本,定期检查大页使用情况,发现异常就手动释放。

知识体系总览

下面这张图,把这一章的核心逻辑串起来了。你可以看到,从内核到硬件,每一层都在为低延迟服务。

操作系统调优知识体系 Linux Kernel 编译选项 RT-Preempt:实时抢占,降低中断响应时间 Tickless:无滴答,消除周期性中断干扰 CPU 资源隔离 isolcpus:隔离核心,独占计算资源 IRQ Affinity:中断绑定,避免干扰交易核心 内存访问优化 HugePages:大页内存,减少TLB Miss 可预测的微秒级延迟

总结一下

这一章的内容,说白了就是三件事:

  • 内核层面:用RT-Preempt和Tickless,让操作系统不再「乱插队」。
  • CPU层面:用isolcpus和IRQ Affinity,让交易程序独占核心。
  • 内存层面:用HugePages,让CPU少跑腿,数据访问更快。

这三步做完,你的交易程序就像住进了VIP包间,系统其他进程再也不会来打扰。延迟从几十微秒降到个位数微秒,完全可期。

最后提醒一句:这些调优不是银弹。如果你的策略本身有缺陷,或者网络链路有瓶颈,操作系统调优也救不了。先确保上层没问题,再动底层。


公众号:蓝海资料掘金营,微信deep3321