操作系统内核调优:让Linux为交易而生
做市商系统跑在Linux上,但默认的Linux内核配置,说白了是为通用服务器准备的。你想让它处理纳秒级的交易?那得动动手术。
我个人习惯,拿到一台新机器,第一件事不是装软件,而是先调内核参数。这就像盖房子先打地基,地基不稳,上层再花哨也白搭。
网络栈优化:别让数据包在路上堵车
做市商最怕什么?网络延迟。一个订单晚到几微秒,可能就错失行情。Linux网络栈默认配置,其实有很多冗余。
net.core.rmem_max 和 net.core.wmem_max
这两个参数控制接收和发送缓冲区的最大值。默认值通常只有几百KB,对于高频交易来说太小了。
# 设置接收缓冲区最大为16MB
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
# 设置TCP读写缓冲区
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
我在项目中遇到过,某次行情爆发时,默认缓冲区直接被打满,丢包率飙升。调大之后,问题立刻解决。
net.core.netdev_budget
这个参数控制CPU一次轮询能从网卡收多少包。默认300,对于做市商来说太保守了。
sysctl -w net.core.netdev_budget=600
嗯,这里要注意,不是越大越好。设得太大,CPU会卡在收包上,其他任务就饿死了。我个人习惯设到600-1000之间,具体看你的流量。
net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_tw_recycle
做市商系统经常大量建立短连接。TIME_WAIT状态会占满端口,导致新连接失败。
sysctl -w net.ipv4.tcp_tw_reuse=1
# 注意:tcp_tw_recycle 在较新内核中已废弃,别用了
我曾经踩过这个坑。某次上线前压测,发现连接数一上去就报错,查了半天才发现是TIME_WAIT太多。开了tcp_tw_reuse后,端口复用,问题解决。
内存管理优化:让数据留在该留的地方
做市商系统对内存延迟极其敏感。Linux默认的内存管理策略,会频繁进行页面回收和交换,这对低延迟应用是灾难。
vm.swappiness
这个参数控制内核使用交换分区的倾向。默认60,意味着内核会尽量把不常用的内存页换到磁盘上。但做市商系统,你希望所有数据都留在物理内存里。
sysctl -w vm.swappiness=1
设成1,内核几乎不会主动交换。除非内存真的不够用了。我建议设成0或1,别设成10,那还是会有交换。
vm.dirty_ratio 和 vm.dirty_background_ratio
这两个参数控制脏页(待写入磁盘的数据)的比例。默认值会导致大量脏页堆积,写入时产生延迟尖峰。
sysctl -w vm.dirty_ratio=10
sysctl -w vm.dirty_background_ratio=5
你想想看,如果脏页太多,内核一次刷盘就会卡住所有IO。做市商系统里,行情数据写入磁盘时,如果被刷盘卡住,那延迟就上去了。调小这两个值,让脏页及时写回,避免突发写入。
调度器优化:让CPU听你的话
Linux默认的CFS调度器,追求公平。但做市商系统,你希望关键线程独占CPU,而不是被其他进程抢走。
isolcpus 内核参数
这个参数可以把指定CPU核心从调度器中隔离出来。说白了,就是告诉内核:这几个核心你别碰,留给我的应用专用。
# 在GRUB_CMDLINE_LINUX中添加
isolcpus=2,3,4,5
隔离后,你需要手动把做市商线程绑定到这些核心上。用taskset或sched_setaffinity都行。
# 将进程PID绑定到CPU 2和3
taskset -cp 2,3 <PID>
我在项目中遇到过,没做隔离前,行情处理线程时不时被其他进程打断,延迟抖动很大。隔离后,抖动基本消失。
中断亲和性
网卡中断默认会落在CPU 0上。如果CPU 0还要处理其他任务,中断响应就会变慢。
你需要把网卡中断绑定到隔离出来的核心上。比如,把eth0的中断绑定到CPU 2和3。
# 查看eth0的中断号
cat /proc/interrupts | grep eth0
# 设置亲和性(假设中断号为 78)
echo 0c > /proc/irq/78/smp_affinity
0c是二进制00001100,表示CPU 2和3。嗯,这里要注意,亲和性设置是位掩码,别搞反了。
大页内存(HugePages):减少TLB缺失
做市商系统通常需要大量内存。默认的4KB页,会导致TLB(页表缓存)频繁缺失。每次TLB缺失,CPU都要去查页表,延迟增加。
大页内存,说白了就是把页大小从4KB变成2MB甚至1GB。TLB能覆盖的内存范围大了几百倍,缺失率大幅下降。
配置HugePages
# 分配1024个2MB大页
sysctl -w vm.nr_hugepages=1024
# 查看是否分配成功
cat /proc/meminfo | grep HugePages
分配后,你的应用需要显式使用大页。比如,用mmap时加上MAP_HUGETLB标志,或者用libhugetlbfs库。
// C++示例:使用大页分配内存
#include <sys/mman.h>
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
-1, 0);
我曾经在一个做市商项目中,把行情数据全部放在大页里。TLB缺失率从5%降到了0.1%以下,延迟直接降了30%。
知识体系总览
下面这张图,是我自己总结的内核调优知识体系。你可以把它当作检查清单,每调完一项就打个勾。
核心思路:内核调优不是玄学,而是让操作系统为你的应用让路。网络栈减少丢包和延迟,内存管理避免交换和抖动,调度器保证关键线程独占CPU,大页内存降低TLB缺失。四者缺一不可。
我的建议:调优时一次只改一个参数,然后观察效果。别一股脑全改了,出了问题都不知道是哪个参数导致的。我曾经就犯过这个错,改完系统直接崩了,查了半天才发现是某个参数设得太激进。
好了,这一章的内容就到这里。内核调优是个细活,别指望一次搞定。多测试,多观察,慢慢找到最适合你系统的配置。
公众号:蓝海资料掘金营,微信deep3321