4. 操作系统调优:内核旁路、中断亲和性与CPU隔离
做高频交易系统,说白了就是在跟操作系统抢时间。
你写的代码再快,如果内核给你来个上下文切换,或者中断把CPU抢走了,那延迟直接崩盘。我个人习惯,拿到一台新机器,第一件事就是把这些“隐形杀手”按死。
4.1 内核旁路:绕过那个“中间商”
为什么需要内核旁路?
标准网络栈,数据从网卡到应用,要经过内核。内核要复制数据、要调度、要切换上下文。这一套下来,几十微秒就没了。在高频交易里,几十微秒够对手抢你十次了。
内核旁路的思路很简单:让应用直接跟网卡对话。
核心手段:
- DPDK (Data Plane Development Kit):Intel 推出的用户态网络库。应用通过 UIO 或 VFIO 直接操作网卡寄存器,绕过内核协议栈。
- Solarflare / Mellanox 的 OpenOnload:硬件厂商的方案,通过内核旁路 + TCP 卸载,延迟能压到 1-2 微秒。
- RDMA (Remote Direct Memory Access):内存直接读写,连 CPU 都绕过了。适合极低延迟场景。
我记得有一次帮客户调优,他们用的标准内核栈,延迟一直在 20 微秒左右徘徊。换成 DPDK 后,直接降到 2 微秒。客户当场就愣住了。说白了,内核就是那个“中间商”,你绕开它,速度自然就上去了。
我的建议:
如果你们用的是标准 Linux 发行版,先别急着上 DPDK。先试试 setsockopt() 里的 SO_BUSY_POLL 和 SO_PRIORITY。有时候软件层面的优化就够了,别一上来就动硬件。
4.2 中断亲和性:别让中断到处乱跑
中断来了,CPU 得停下手里的活去处理。如果中断每次都落在不同的 CPU 上,那缓存就全废了。
中断亲和性,就是把特定设备的中断绑定到固定的 CPU 核心上。这样,那个核心的 L1/L2 缓存里,永远存着中断处理的热数据。
怎么配?
# 查看网卡中断号
cat /proc/interrupts | grep eth0
# 设置中断亲和性(假设中断号 78,绑定到 CPU 0)
echo 1 > /proc/irq/78/smp_affinity
# 或者用 irqbalance 工具(但建议关掉它,手动绑定更可控)
systemctl stop irqbalance
systemctl disable irqbalance
我曾经踩过一个坑:某次上线前,发现延迟抖动特别大。查了半天,发现是 irqbalance 在后台自动迁移中断。关掉它,手动绑定后,抖动直接消失。嗯,这里要注意:生产环境一定要关掉 irqbalance。
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| irqbalance | 关闭 | 手动绑定更可控 |
| smp_affinity | 绑定到专用核心 | 避免中断干扰业务 |
| RPS/RFS | 关闭 | 软件分发增加延迟 |
4.3 CPU 隔离:给关键任务留个“单间”
操作系统默认会公平调度所有进程。但公平,对高频交易来说就是灾难。你想想看,你的做市线程正在处理行情,突然被调度出去,让给一个日志进程。那几毫秒的切换,可能就错过了一笔交易。
CPU 隔离,就是把某些核心从内核的调度器中“摘”出来。这些核心只运行你指定的任务,其他进程和线程都不能碰。
Linux 内核提供了 isolcpus 启动参数:
# 在 GRUB 配置中添加
isolcpus=2,3
# 然后使用 taskset 绑定你的做市线程到这些核心
taskset -c 2,3 ./market_maker
更狠一点,可以用 cpuset 子系统:
# 创建 cpuset
mkdir /sys/fs/cgroup/cpuset/hft
echo 2-3 > /sys/fs/cgroup/cpuset/hft/cpuset.cpus
echo 0 > /sys/fs/cgroup/cpuset/hft/cpuset.mems
# 把进程 PID 加入
echo $PID > /sys/fs/cgroup/cpuset/hft/cgroup.procs
警告:
隔离 CPU 后,那个核心上的内核线程(比如 ksoftirqd)也会被隔离。如果中断处理不及时,可能导致网络丢包。我建议至少留一个核心给内核,别全隔离了。
4.4 知识体系图:延迟优化的“三板斧”
下面这张图,是我自己总结的延迟优化路径。从硬件到软件,层层递进。
这张图里,操作系统层是承上启下的关键。硬件再好,系统没调好,延迟照样下不去。反过来,系统调好了,哪怕硬件差一点,也能跑出不错的成绩。
4.5 避坑指南:我踩过的那些坑
- 超线程(Hyper-Threading):我建议关掉。超线程共享 L1/L2 缓存,一个核心上的两个逻辑线程会互相干扰。延迟敏感场景,关掉它。
- NUMA 亲和性:内存访问有远近之分。你的做市线程和它用的网卡、内存,最好在同一个 NUMA 节点上。用
numactl绑定。 - 内核版本:别用太新的内核。有些“优化”反而引入延迟。我个人习惯用长期支持版(LTS),比如 5.10 或 5.15。
- 电源管理:把 CPU 频率调到最高,关闭节能模式。用
cpupower设置performance策略。
一个小技巧:
调优完成后,用 perf stat 看看上下文切换次数。如果每秒超过 1000 次,说明还有优化空间。我一般压到 100 次以下才算合格。
操作系统调优,说白了就是“减少干扰”。把中断、调度、缓存污染这些干扰降到最低,你的系统才能跑出极限性能。嗯,这一章的内容就到这里。下一章我们聊聊应用层的优化——无锁编程和内存管理。