4. 操作系统内核调优:Linux内核参数优化、中断亲和性、CPU隔离与频率锁定
做期权做市,说白了就是跟时间赛跑。你的策略再牛,模型再准,如果操作系统这一层拖了后腿,那一切都白搭。我见过太多团队,花大价钱买硬件,结果内核参数没调,延迟直接翻倍。今天我们就来聊聊,怎么把Linux内核这块「磨刀石」给磨锋利了。
核心思路:操作系统调优的目标只有一个——让交易线程独占CPU,并且不被任何东西打断。中断、调度、时钟、电源管理,统统要让路。
4.1 Linux内核参数优化
内核参数是操作系统的「开关」。调对了,延迟降一半;调错了,系统崩给你看。我个人习惯,先改网络相关的,再改内存和调度相关的。
4.1.1 网络栈参数
做市系统最怕网络延迟抖动。以下是我在实盘环境中常用的参数组合:
# 减少TCP连接TIME_WAIT数量
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
# 增大TCP接收/发送缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 启用TCP快速打开(TFO)
net.ipv4.tcp_fastopen = 3
# 关闭Nagle算法(小包不等待)
net.ipv4.tcp_nodelay = 1
# 增大网卡队列长度
net.core.netdev_max_backlog = 50000
# 关闭反向路径过滤(多网卡场景)
net.ipv4.conf.all.rp_filter = 0
避坑指南:我曾经在某个交易所的机房调试时,发现tcp_tw_reuse虽然能减少TIME_WAIT,但在NAT环境下会导致连接异常。后来我改用了tcp_tw_recycle(内核4.12后已移除),现在更推荐用SO_REUSEADDR在应用层处理。
4.1.2 内存与调度参数
内存分配和进程调度,是延迟的隐形杀手。你想想看,一个malloc调用如果触发了缺页中断,那延迟直接飙到毫秒级。做市系统可受不了这个。
# 禁用透明大页(THP)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 减少swappiness,避免内存交换
vm.swappiness = 1
# 增大内存映射区域
vm.max_map_count = 655300
# 调度器设置为实时优先级
kernel.sched_rt_runtime_us = -1
kernel.sched_rt_period_us = 1000000
这里重点说下透明大页。很多新手觉得「大页=高性能」,其实不然。THP在后台做内存合并时,会触发CPU停顿。我在做回测系统时,就因为这个原因,延迟从5微秒跳到了200微秒。排查了整整两天才找到原因。
4.2 中断亲和性
网卡中断来了,CPU得停下手里的活去处理。如果中断落在你的交易线程上,那延迟就不可控了。中断亲和性,就是把中断绑定到指定的CPU核心上,让交易线程独占其他核心。
4.2.1 查看当前中断分布
# 查看所有中断的CPU亲和性
cat /proc/interrupts | grep eth0
# 查看某个中断的smp_affinity
cat /proc/irq/<irq_number>/smp_affinity
4.2.2 设置中断亲和性
假设你有4个CPU核心(0-3),交易线程跑在核心2和3上。那么网卡中断应该绑定到核心0和1。
# 将中断绑定到CPU0(十六进制0x01)
echo 1 > /proc/irq/<irq_number>/smp_affinity
# 将中断绑定到CPU0和CPU1(十六进制0x03)
echo 3 > /proc/irq/<irq_number>/smp_affinity
注意:smp_affinity使用十六进制位图。CPU0对应bit0,CPU1对应bit1,以此类推。写错了系统不会报错,但中断可能落在你不想让它落的地方。我曾经犯过这个错,结果交易线程被中断打搅,延迟曲线出现了规律的毛刺。
4.2.3 使用irqbalance?我劝你关掉
irqbalance这个服务,初衷是让中断负载均衡。但在低延迟场景下,它就是个捣乱的。它会动态迁移中断,导致延迟抖动。我个人习惯,直接关掉:
systemctl stop irqbalance
systemctl disable irqbalance
4.3 CPU隔离
CPU隔离,就是把某些核心从Linux调度器的管理范围中移除。这样,除了你指定的线程,其他任何进程都不能跑到这些核心上。
4.3.1 内核启动参数
在GRUB配置中,添加isolcpus参数:
# 隔离CPU2和CPU3
isolcpus=2,3
# 同时隔离rcu回调
rcu_nocbs=2,3
# 关闭CPU2和CPU3的时钟事件
nohz_full=2,3
这三个参数要一起用。isolcpus负责隔离,rcu_nocbs避免RCU中断,nohz_full关闭周期性时钟。缺一个,效果都会打折扣。
4.3.2 验证隔离效果
# 查看进程是否跑在隔离核心上
taskset -cp <pid>
# 将交易线程绑定到隔离核心
taskset -c 2,3 ./trading_engine
小技巧:隔离后,可以用stress工具压一下其他核心,然后观察交易线程的延迟。如果延迟没有明显变化,说明隔离生效了。我一般会跑个24小时的压力测试,确保万无一失。
4.4 频率锁定
CPU频率动态调整,是现代CPU的省电技术。但做市系统不需要省电,需要的是稳定。频率忽高忽低,会导致指令执行时间不一致,延迟自然就抖了。
4.4.1 关闭频率缩放驱动
# 查看当前频率策略
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# 设置为performance模式
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo performance > $cpu
done
4.4.2 锁定频率到最大值
# 查看可用频率
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
# 锁定到最高频率(假设是3.2GHz)
echo 3200000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
echo 3200000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
警告:频率锁定后,CPU会一直跑在最高频,功耗和发热会明显增加。记得检查散热,别把CPU烧了。我在实验室里就遇到过,锁定频率后没注意散热,结果CPU降频保护,延迟反而更差了。
4.5 知识体系总览
说了这么多,我画张图帮你理一下思路。内核调优不是孤立的事,它是一套组合拳。
4.6 实战检查清单
调优不是一次性的活。每次系统变更后,我建议按这个清单检查一遍:
| 检查项 | 命令/方法 | 预期结果 |
|---|---|---|
| THP是否关闭 | cat /sys/kernel/mm/transparent_hugepage/enabled | 输出 [never] |
| 中断是否绑定 | cat /proc/interrupts | grep eth0 | 中断集中在非交易核心 |
| CPU是否隔离 | cat /sys/devices/system/cpu/isolated | 显示隔离的核心编号 |
| 频率是否锁定 | cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq | 所有核心频率一致且为最大值 |
最后说一句:内核调优是个细致活。别指望一次改完所有参数就能跑出最佳性能。我一般会先改一组参数,跑个压力测试,观察延迟分布。然后再改下一组。这样出了问题,也知道是哪个参数导致的。嗯,稳扎稳打,才是做市系统的生存之道。