3、CPU隔离与亲和性:把风控线程关进“专属包厢”
各位做高频交易的朋友,咱们今天聊一个硬实时系统里绕不开的话题——CPU隔离与亲和性。
说白了,就是怎么给你的风控线程找个“专属包厢”,不让别的进程来打扰它。
我刚开始做量化系统那会儿,总觉得只要代码写得快,CPU调度自然会给面子。结果呢?有一次实盘测试,风控逻辑突然被一个后台日志进程打断了,延迟直接飙到200微秒。嗯,从那以后,我再也不敢小看CPU亲和性这回事了。
3.1 为什么需要CPU隔离?
你想想看,一个标准的Linux服务器上,跑着几十上百个进程。内核调度器像个忙碌的餐厅经理,不停地把各个进程分配到不同的CPU核心上。今天你的风控线程在核0上跑,明天可能就被赶到核3上去了。
这种“流浪”会带来两个问题:
- 缓存污染:每次迁移到新核,L1/L2缓存都得重新加载,白白浪费几十个微秒
- 中断干扰:其他进程产生的中断,可能打断你的风控计算
我个人习惯,是把风控线程当作“VIP客户”来对待。给它一个固定的核心,谁也不许碰。
3.2 isolcpus内核参数:从源头隔离
isolcpus是Linux内核启动参数,它告诉调度器:“这几个核心你别管,我来安排。”
使用方法很简单,在GRUB配置里加上:
isolcpus=2,3
这表示核2和核3被隔离出来,普通进程不会自动分配到这两个核心上。
但注意,isolcpus只是“建议”。内核仍然可能因为某些特殊情况(比如中断处理)把任务扔到隔离核上。所以它是个基础,但不是全部。
3.3 cpuset控制组:更精细的隔离
如果说isolcpus是“大砍刀”,那cpuset就是“手术刀”。
cpuset是cgroup的一个子系统,可以精确控制一组进程能使用哪些CPU核心和内存节点。
我一般这样用:
# 创建cpuset组
mkdir /sys/fs/cgroup/cpuset/risk_ctrl
# 分配核心2和3给这个组
echo "2,3" > /sys/fs/cgroup/cpuset/risk_ctrl/cpuset.cpus
# 分配内存节点(通常和CPU在同一NUMA节点)
echo "0" > /sys/fs/cgroup/cpuset/risk_ctrl/cpuset.mems
# 把风控进程PID加进去
echo 12345 > /sys/fs/cgroup/cpuset/risk_ctrl/cgroup.procs
这样做的好处是:即使系统里还有其他进程,它们也抢不到核2和核3。风控线程可以安心独享。
| 方法 | 隔离粒度 | 灵活性 | 推荐场景 |
|---|---|---|---|
| isolcpus | 粗粒度(整个核心) | 低(需重启) | 生产环境长期固定 |
| cpuset | 细粒度(进程级别) | 高(动态调整) | 开发调试、动态分配 |
3.4 绑定风控线程到专用核
隔离完核心,下一步就是把风控线程“钉”上去。这里用pthread亲和性设置:
#include <sched.h>
#include <pthread.h>
void bind_to_core(int core_id) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(core_id, &cpuset);
pthread_t current_thread = pthread_self();
int ret = pthread_setaffinity_np(current_thread,
sizeof(cpu_set_t),
&cpuset);
if (ret != 0) {
// 处理错误,我一般直接abort
perror("pthread_setaffinity_np failed");
exit(EXIT_FAILURE);
}
}
调用方式:
// 在风控线程入口处调用
void* risk_control_loop(void* arg) {
bind_to_core(2); // 绑定到核2
while(1) {
// 风控逻辑...
}
}
这里有个细节:绑定要尽早做。最好在线程创建后的第一件事就是设置亲和性。因为在线程创建初期,它可能已经被调度到别的核心上跑过几毫秒了,缓存已经乱了。
3.5 避免核间干扰
隔离和绑定都做了,是不是就高枕无忧了?
不一定。还有几个“隐形杀手”要注意:
- 中断亲和性:网卡、磁盘的中断可能打到你的隔离核上。用/proc/irq/调整中断亲和性,把中断赶到非隔离核上
- 内核线程:有些内核线程(比如kswapd)可能跑到隔离核上。用cpuset把它们也限制住
- 超线程:同一个物理核心的两个逻辑核会共享L1缓存。如果另一个逻辑核跑着其他任务,你的风控线程可能被“拖累”。我建议关闭超线程,或者把两个逻辑核都分配给风控
我曾经遇到过一个诡异的问题:风控线程偶尔会抖动几十微秒。查了三天,最后发现是某个监控agent的定时任务,每隔30秒扫描一次/proc文件系统,恰好扫到了隔离核。嗯,从那以后,所有监控agent我都用cpuset限制到非隔离核上。
3.6 知识体系总览
下面这张图,是我自己总结的CPU隔离与亲和性的完整流程:
这张图把整个流程串起来了。从目标出发,到三大手段,再到具体操作,最后是避坑指南。你照着这个思路做,基本不会出大问题。
好了,这一章就到这里。记住:给风控线程一个安稳的家,它才能给你稳稳的回报。