3. 操作系统调优:让Linux为交易而生

做市商系统跑在Linux上,但默认的Linux内核参数,说白了是为通用服务器设计的。你想让它为高频交易服务?那必须得调。我个人习惯是,拿到一台新机器,第一件事就是把这些参数改掉,不然心里不踏实。

3.1 网络缓冲区:rmem_default 与 wmem_default

这两个参数控制着TCP套接字的默认接收和发送缓冲区大小。默认值通常只有几十KB,对于做市商系统来说,太小了。

为什么太小会出问题?

你想想看,行情数据一来就是一大波,如果缓冲区太小,内核来不及处理,数据就会被丢弃。丢一个tick,你的策略可能就少赚几十万。我在项目中遇到过,某次行情爆发时,系统丢包率飙升到5%,排查了半天,最后发现就是缓冲区太小。

核心参数说明

参数 默认值 推荐值(做市商场景) 说明
net.core.rmem_default 212992 (约208KB) 4194304 (4MB) 接收缓冲区默认大小
net.core.wmem_default 212992 (约208KB) 4194304 (4MB) 发送缓冲区默认大小
net.core.rmem_max 212992 16777216 (16MB) 接收缓冲区最大值
net.core.wmem_max 212992 16777216 (16MB) 发送缓冲区最大值

设置方法很简单,直接改 /etc/sysctl.conf 文件:

# 网络缓冲区调优 - 做市商专用
net.core.rmem_default = 4194304
net.core.wmem_default = 4194304
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# 生效
sysctl -p

我的经验:别把缓冲区设得太大,比如直接上64MB。太大反而会增加内存拷贝开销,延迟反而变高。4MB到16MB这个区间,是我在多个项目中验证过的黄金区间。

3.2 中断亲和性:让网卡专心服务指定CPU

网卡收到数据包后,会触发硬件中断。默认情况下,这个中断可能落在任何一个CPU核心上。你想想看,如果中断落在正在运行策略的CPU上,策略执行就被打断了,延迟瞬间飙升。

怎么做?

把网卡的中断绑定到专用的CPU核心上。比如你有16个核心,可以把核心0-3留给操作系统,核心4-7给策略,核心8-11给网卡中断处理。

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

# 假设中断号是 78-85,绑定到 CPU 8-11
echo 0f0 > /proc/irq/78/smp_affinity
echo 0f0 > /proc/irq/79/smp_affinity
# ... 以此类推

这里的 0f0 是十六进制掩码,表示CPU 8-11。嗯,这里要注意,掩码的计算方式很容易搞错,我建议你写个脚本自动生成。

避坑指南:我曾经犯过一个低级错误——把中断绑定到了正在运行策略的CPU上。结果策略延迟从5微秒飙到了50微秒。排查了整整一天才发现是中断亲和性配反了。所以,配完之后一定要用 cat /proc/interrupts 确认一下。

3.3 CPU隔离(isolcpus):给策略一个专属舞台

Linux内核默认会在所有CPU核心上调度进程和中断。这对做市商系统来说简直是灾难。你希望策略在一个核心上稳定运行,结果内核突然把别的进程调度过来,或者跑来处理中断,策略的执行时间就不确定了。

isolcpus 就是干这个的。

它告诉内核:这些CPU核心你别碰,留给用户态程序独占。设置方法是在内核启动参数里加:

# 在 /etc/default/grub 中修改 GRUB_CMDLINE_LINUX
GRUB_CMDLINE_LINUX="isolcpus=4-11 nohz_full=4-11 rcu_nocbs=4-11"

# 更新grub
grub2-mkconfig -o /boot/grub2/grub.cfg

这里我隔离了CPU 4-11,一共8个核心。同时配合 nohz_fullrcu_nocbs,进一步减少内核干扰。

关键点:

  • isolcpus:内核不会在这些核心上调度普通进程
  • nohz_full:关闭这些核心的定时器中断,减少抖动
  • rcu_nocbs:把这些核心从RCU回调中排除,进一步降低延迟

我个人习惯是,把物理核心的一半留给策略,另一半给操作系统和中断处理。比如32核的机器,我隔离16个核心给策略,剩下的16个跑系统服务和中断。

3.4 大页内存:减少TLB miss,提升内存访问速度

做市商系统通常需要管理大量的订单簿数据,这些数据在内存中频繁访问。默认的4KB内存页,会导致大量的TLB(页表缓存)缺失。每次TLB miss,CPU都要去查页表,延迟增加几十纳秒。

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

# 配置大页内存(2MB页)
# 分配1024个大页,总共2GB
echo 1024 > /proc/sys/vm/nr_hugepages

# 或者通过 /etc/sysctl.conf 持久化
vm.nr_hugepages = 1024

然后在你的程序里,用 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);

我的经验:大页内存不是越多越好。分配太多会浪费物理内存,分配太少又不够用。我一般先跑一个压力测试,看看策略实际需要多少内存,然后在此基础上加20%的余量。

另外,从Linux 2.6.38开始,还有一种透明大页(Transparent HugePages),它会自动把普通内存页合并成大页。但我建议你关掉它。为什么?因为它的合并过程本身就会引入延迟抖动,对做市商系统来说得不偿失。

# 关闭透明大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

3.5 本章知识体系

下面这张图,把本章的核心逻辑串起来了。你可以看到,四个调优方向分别解决了什么问题,最终目标都是降低延迟、减少抖动。

Linux内核调优知识体系 做市商系统性能优化 网络缓冲区调优 中断亲和性设置 CPU隔离 (isolcpus) 大页内存配置 rmem_default / wmem_default 防止行情数据丢包 绑定网卡中断到专用CPU 避免中断打断策略执行 isolcpus + nohz_full 消除内核调度抖动 2MB/1GB大页 减少TLB miss 最终目标:确定性延迟 + 零抖动 每个tick的处理时间可预测,不受系统干扰 四个方向缺一不可,共同构建低延迟运行环境

这四个方向,说白了就是给做市商系统打造一个「干净」的运行环境。网络缓冲区保证数据不丢,中断亲和性保证策略不被打扰,CPU隔离保证执行时间稳定,大页内存保证内存访问快速。缺一个,你的系统延迟就可能出现毛刺。

一句话总结:调优不是玄学,是科学。每一个参数背后都有明确的物理意义。理解它,用好它,你的做市商系统才能真正跑在「快车道」上。


公众号:蓝海数据掘金营,微信deep3321