3、操作系统与内核优化:Linux内核参数调优

做期权做市,说白了就是跟时间赛跑。你的策略再牛,模型再准,如果操作系统这一层拖了后腿,一切都白搭。我见过太多团队,花大价钱买硬件,结果内核参数没调,延迟直接多出几十微秒。嗯,这几十微秒在撮合引擎里,可能就是几档价差。

今天咱们就聊聊Linux内核那几个最关键的调优点。我个人习惯,每搭建一套新的做市系统,第一件事就是先把这些参数锁死。

核心目标:把CPU资源从内核的「公平调度」中抢回来,全部喂给我们的做市进程。

3.1 CPU隔离与独占:isolcpus

先问一个问题:你的做市进程跑在CPU 2上,突然内核要处理一个网络中断,它会不会把CPU 2上的进程踢下去?会的。这就是典型的「噪声」。

isolcpus 这个内核启动参数,就是用来解决这个问题的。它告诉内核:这几个CPU你别碰,别往上面调度任何用户态或内核态的任务。

# 在 /etc/default/grub 中修改 GRUB_CMDLINE_LINUX
GRUB_CMDLINE_LINUX="isolcpus=2,3,4,5 nohz_full=2,3,4,5 rcu_nocbs=2,3,4,5"

我在项目中遇到过一件事:一开始只隔离了2个核,结果做市进程偶尔还是会出现毫秒级的抖动。排查了半天,发现是内核的RCU(Read-Copy Update)回调在作祟。所以后来我习惯把 rcu_nocbs 也加上,把RCU回调也从这个核上移走。

避坑指南:我曾经以为隔离了CPU就万事大吉,结果发现系统定时器中断(timer tick)还是会打断隔离核。这时候就需要 nohz_full 出场了。

3.2 无滴答模式:nohz_full

Linux内核默认每个CPU每秒都会收到几百次定时器中断(tick)。这个tick用来做进程调度、负载统计、时间记账。但对于做市进程来说,这些全是噪音。

nohz_full 参数可以让隔离的CPU完全关闭周期性tick。只有在有任务运行时,才按需触发。说白了,就是让CPU彻底安静下来。

# 查看当前CPU的tick模式
cat /proc/timer_list | grep .event_handler

# 如果看到 tick_nohz_handler,说明 nohz_full 生效了

你想想看,一个做市进程在微秒级别处理订单,结果每1毫秒被tick打断一次,这延迟能低才怪。我个人的经验是,nohz_full 配合 isolcpus 一起用,能把抖动从几十微秒降到个位数微秒。

注意:nohz_full 只对隔离核生效。如果你没有设置 isolcpus,光设 nohz_full 是没用的。另外,至少留一个核给内核做 housekeeping,别把所有核都隔离了。

3.3 中断亲和性设置

网卡中断来了,谁来处理?默认情况下,中断可能落在任何一个CPU上。如果落在你的做市核上,那就糟了——进程被抢占,延迟飙升。

解决方案很简单:把网卡中断绑定到非隔离的CPU上。

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

# 假设中断号是 78,把它绑定到 CPU 0
echo 1 > /proc/irq/78/smp_affinity

# 或者用 set_irq_affinity 脚本批量设置
./set_irq_affinity 0 eth0

这里有个细节:smp_affinity 用的是位图。比如绑定到CPU 0,就写1(二进制0001);绑定到CPU 1,就写2(二进制0010)。我刚开始做的时候老搞混,后来干脆写了个脚本自动算。

我的习惯:把网卡中断、存储中断全部赶到CPU 0和CPU 1上。做市进程独占CPU 2-5。这样中断再闹,也闹不到我的核心业务上。

3.4 内存大页:HugePages

做市系统里,内存访问延迟是另一个大头。TLB(页表缓存)命中率低的时候,每次内存访问都要查页表,那延迟就上去了。

HugePages 就是把内存页从默认的4KB放大到2MB甚至1GB。页大了,同样的内存区域需要的页表项就少了,TLB的覆盖范围就大了。

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

# 查看是否分配成功
cat /proc/meminfo | grep HugePages

# 输出示例:
# HugePages_Total:    1024
# HugePages_Free:     1024
# HugePages_Rsvd:        0
# HugePages_Surp:        0

在代码里,用 mmap 加上 MAP_HUGETLB 标志,就能直接分配大页内存。

#include <sys/mman.h>

void* buf = mmap(NULL, 2 * 1024 * 1024,
                 PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
                 -1, 0);

我记得有一次,一个同事抱怨他的做市引擎延迟不稳定。我一看,内存没用大页,TLB miss率高达5%。换成大页之后,直接降到0.1%以下。嗯,有时候问题就这么简单。

关键数据:使用HugePages后,TLB miss率通常能降低90%以上。对于做市这种高频内存访问的场景,收益非常明显。

3.5 本章知识体系

下面这张图,是我自己总结的优化链路。从硬件中断到进程调度,再到内存访问,每一层都有对应的优化手段。

Linux内核优化链路 硬件中断层 网卡/存储中断 → 中断亲和性设置 → 绑定到非隔离CPU CPU调度层 isolcpus → 隔离核心 → nohz_full → 关闭定时器中断 内存访问层 HugePages → 减少TLB miss → 降低内存访问延迟 最终效果:低延迟、低抖动、确定性执行

这三层优化,每一层都不可或缺。中断层没处理好,你的做市核随时可能被抢;调度层没处理好,定时器中断会持续制造抖动;内存层没处理好,TLB miss带来的延迟会吃掉你所有的优化成果。

一句话总结:先隔离CPU,再关tick,最后绑中断、开大页。顺序别搞反了。


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