3、操作系统内核调优:Linux内核参数优化与实时内核对比
说到操作系统内核调优,我个人的经验是——这往往是整个交易系统性能链条里最容易被忽视的一环。很多团队把精力全砸在应用层代码优化上,结果发现延迟就是降不下来。其实,内核才是那个真正掌控硬件资源的「大管家」。
今天咱们就聊聊三块核心内容:网络、内存、调度器的内核参数怎么调,以及实时内核和低延迟内核到底该怎么选。
3.1 网络栈参数优化
交易系统对网络延迟极其敏感。毫秒级的抖动,可能就是几百万的损失。我见过不少团队,网卡用的是Solarflare,驱动也换了,结果延迟还是不稳定。查到最后,问题出在内核参数上。
3.1.1 套接字缓冲区调大
先看几个关键参数:
# 增大TCP读写缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# 设置TCP自动缓冲区范围
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 启用TCP窗口缩放
net.ipv4.tcp_window_scaling = 1
为什么要调大?说白了,交易系统里高频的订单流和行情数据,都是突发性的。缓冲区太小,内核就得频繁做内存分配和回收,延迟自然就上去了。
3.1.2 减少中断与软中断开销
网络包进来,CPU要处理中断。中断太多,业务线程就被打断了。你想想看,一个微秒级的行情处理流程,被中断打断几次,延迟就崩了。
# 启用RPS(Receive Packet Steering)
net.core.rps_sock_flow_entries = 32768
# 启用XPS(Transmit Packet Steering)
net.core.xps_cpus = 0x00000001 # 绑定到CPU0
# 减少TIME_WAIT套接字数量
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
这里有个坑——tcp_tw_reuse 和 tcp_tw_recycle 容易搞混。我建议只用 tcp_tw_reuse,别碰 tcp_tw_recycle。后者在NAT环境下会导致严重的丢包问题,我踩过这个坑,修了整整两天。
3.1.3 网卡多队列与中断亲和性
现代网卡都支持多队列。每个队列可以绑定到不同的CPU核心。这样中断就能分散处理,避免单核过载。
# 查看网卡队列数
ethtool -l eth0
# 设置每个队列的中断亲和性
echo 1 > /proc/irq/78/smp_affinity # 队列0绑定CPU0
echo 2 > /proc/irq/79/smp_affinity # 队列1绑定CPU1
irqbalance 服务关闭自动平衡。否则内核会自作主张把中断迁移到其他核心,你的绑定就白做了。
3.2 内存管理参数优化
交易系统的内存访问模式很特殊——大量的小对象分配和释放,而且对延迟极度敏感。默认的Linux内存管理策略,其实是为通用服务器设计的,不适合我们。
3.2.1 减少内存碎片
我建议开启透明大页(THP),但要注意方式:
# 启用透明大页
echo always > /sys/kernel/mm/transparent_hugepage/enabled
# 但关闭碎片整理(关键!)
echo never > /sys/kernel/mm/transparent_hugepage/defrag
为什么关闭碎片整理?因为THP的碎片整理是同步的,它会阻塞当前线程,直到内存整理完成。我遇到过几次,行情处理线程突然卡住几十毫秒,查到最后就是THP碎片整理在作祟。
3.2.2 减少swap与OOM风险
# 降低swap倾向性
vm.swappiness = 1
# 增加内存超分配比例
vm.overcommit_memory = 1
vm.overcommit_ratio = 50
# 减少脏页回写延迟
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5
swappiness 设为1,意味着内核几乎不会主动swap。交易系统里,内存就是命,swap一次可能就导致订单超时。我习惯在压测时监控 /proc/meminfo 里的 SwapCached 字段,一旦非零,说明内存已经吃紧了。
3.2.3 NUMA感知与内存绑定
现在的服务器基本都是NUMA架构。CPU访问本地内存和远端内存,延迟能差一倍。交易系统必须做NUMA感知。
# 查看NUMA拓扑
numactl --hardware
# 启动进程时绑定到指定节点
numactl --cpunodebind=0 --membind=0 ./trading_engine
3.3 调度器参数优化
调度器决定了哪个线程什么时候运行。交易系统里,行情处理线程和订单发送线程必须优先运行。默认的CFS调度器(完全公平调度)追求的是公平,而不是低延迟。
3.3.1 使用实时调度策略
# 设置线程为FIFO实时调度,优先级99
chrt -f -p 99 <pid>
# 或者在代码中设置
struct sched_param param;
param.sched_priority = 99;
pthread_setschedparam(thread, SCHED_FIFO, ¶m);
这里有个关键点——实时优先级范围是0-99,数字越大优先级越高。我建议把最关键的行情处理线程设为99,订单发送线程设为98,其他辅助线程设为50以下。千万别把所有线程都设成99,否则高优先级线程之间互相抢占,反而会出问题。
3.3.2 调整CFS调度器参数
如果不想用实时调度(有些场景不允许),可以调优CFS:
# 减少调度延迟
kernel.sched_min_granularity_ns = 1000000 # 1ms
kernel.sched_latency_ns = 4000000 # 4ms
kernel.sched_wakeup_granularity_ns = 500000 # 0.5ms
这些参数控制了CFS调度器的最小时间片和唤醒延迟。调小之后,线程被唤醒后能更快获得CPU。但代价是上下文切换变多,CPU利用率会上升。嗯,这是个权衡。
3.3.3 CPU隔离与独占
最极致的做法,是把某些CPU核心完全隔离出来,只给交易线程用。连内核自己的进程都不能上去。
# 在grub启动参数中隔离CPU2-7
isolcpus=2,3,4,5,6,7 nohz_full=2,3,4,5,6,7 rcu_nocbs=2,3,4,5,6,7
隔离之后,再用 taskset 把交易线程绑定到这些核心上。这样就能做到几乎零干扰。我在一个做市商项目里用过这套方案,延迟抖动从原来的50微秒降到了5微秒以内。
3.4 实时内核 vs 低延迟内核
这是很多团队纠结的问题。我直接说结论:
| 特性 | 实时内核(PREEMPT_RT) | 低延迟内核(Low-Latency) |
|---|---|---|
| 抢占模型 | 完全可抢占(包括中断处理) | 自愿抢占(内核代码不可抢占) |
| 最大延迟 | < 10微秒 | < 100微秒 |
| 吞吐量 | 降低约5-15% | 几乎无影响 |
| 适用场景 | 硬实时交易、FPGA协同 | 普通高频交易、行情处理 |
| 维护成本 | 高(需打补丁、定制内核) | 低(发行版自带) |
我的建议很明确:
- 如果你做的是纳秒级的高频交易,比如FPGA+CPU协同处理,那必须上PREEMPT_RT。它的确定性延迟是其他方案给不了的。
- 如果你做的是普通的高频交易或算法交易,延迟要求在几十微秒级别,那低延迟内核就够了。PREEMPT_RT的吞吐量损失和运维成本,不值得。
我记得有个团队,为了追求极致延迟,上了PREEMPT_RT。结果发现行情处理吞吐量下降了10%,导致行情积压。最后换回低延迟内核,配合CPU隔离,效果反而更好。所以,别盲目追求「实时」,要看你的业务场景。
3.5 实战:一套可用的内核参数配置
最后,给出一套我常用的配置模板。你可以根据实际情况微调:
# /etc/sysctl.d/99-trading.conf
# 网络优化
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rps_sock_flow_entries = 32768
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
# 内存优化
vm.swappiness = 1
vm.overcommit_memory = 1
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5
# 调度器优化
kernel.sched_min_granularity_ns = 1000000
kernel.sched_latency_ns = 4000000
kernel.sched_wakeup_granularity_ns = 500000
# 关闭不必要的特性
net.ipv4.tcp_sack = 0
net.ipv4.tcp_dsack = 0
net.ipv4.tcp_fack = 0
应用配置:sysctl -p /etc/sysctl.d/99-trading.conf
嗯,这套配置我在多个项目里验证过。它能帮你把延迟抖动降低一个数量级。但记住,没有银弹。每套系统都有自己的瓶颈,你需要用 perf、ftrace 这些工具去定位,然后针对性调优。
下一章,咱们聊聊更底层的——CPU缓存优化和内存屏障。那才是真正考验架构师功底的地方。