ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux CPU亲和性实战:taskset命令从原理到生产环境调优

Linux CPU亲和性实战:taskset命令从原理到生产环境调优 扛过线上事故的人对 Linux 的 CPU 亲和性肯定不会陌生。某次压测流量上来我发现某个核心服务被内核在不同 CPU 之间来回切换cache 命中率惨不忍睹延迟直接翻了一倍。用taskset把进程钉在固定的几个核心上之后吞吐立刻稳住了。这个命令看着简单但背后的原理和落地细节比想象中要多不少本文就把它彻底讲透。不管你是运维、后端开发还是做性能调优的只要你的服务跑在多核 Linux 机器上taskset都应该躺进你的常用工具箱。它既能查看进程当前运行在哪个 CPU 上也能在启动时或运行中把进程绑定到指定核心是排查 CPU 占用异常、优化缓存局部性、隔离关键业务进程的一把好手。下面从原理讲到实战再讲到生产环境的进阶玩法。1. CPU亲和性到底解决什么问题1.1 先理解“进程在 CPU 间乱跑”的代价Linux 内核默认的调度策略是“负载均衡优先”。系统里有 8 个核心一个进程刚醒来要运行调度器会倾向于找当前最空闲的那个 CPU 把任务塞过去。这套逻辑在普通服务器上很合理——做运维的都知道top里看到 8 个核各自忙活谁也不想让某些核闲着想别的核排队。但代价在于一个刚在 CPU0 上运行完的进程下次调度可能被安排到 CPU7。此时它的 L1/L2 缓存里全是上次运行留下的热数据TLB页表缓存也还记着它最常访问的地址映射结果换了个核心这一切全部作废得重新从内存甚至磁盘缓存里把数据弄进来。大量核心线程频繁跨核迁移cache miss 成了常态原本几分钟能跑完的任务愣是拖到了十几分钟。这个问题在做分布式缓存、计算密集型的 Java 服务、或者依赖 CPU 绑定发挥性能的数据库实例时尤其明显。网络上随手搜“cpu 亲和性”能看到大量性能分析案例核心原因基本都指向同一个点上下文切换的隐性成本被低估了。1.2 亲和性的两种形态软亲和与硬亲和taskset实际修改的是进程的 CPU 亲和性掩码affinity mask这个掩码告诉内核这个进程/线程允许在哪些 CPU 上运行。它有“软”“硬”两种层面的含义理解透了后面排查问题才不迷糊。所谓“软亲和”是指面向单次的唤醒或迁移决策。内核在调度的时候会尽量保持一个进程留在它上次运行的 CPU 上这是一种倾向不是强制。即便你什么都没设置系统也会尽量减少不必要的迁移。这个机制叫 cache affinity。所谓“硬亲和”就是通过sched_setaffinity()系统调用、或者命令行下的taskset明确设置一个 CPU 列表。一旦设置内核的调度器只能在允许的 CPU 集合里选择目标核心其他核心哪怕空闲到冒烟也不会把进程调度过去。这是本篇文章的主角。从应用角度去看软亲和是系统“顺手”帮你做的事硬亲和则是你主动干预进程的生死运行权。性能敏感的服务往硬亲和方向靠拢是对的但“啥都绑核心”同样会踩坑这点后面有一节专门讲。2. Linux内核到底是怎么处理亲和性的2.1 CPU 位图与调度器交互内核在进程描述符task_struct里保存了一个字段叫cpus_allowed实质上就是一个 CPU 位图cpumask。每个 bit 对应一个逻辑 CPU 编号bit0 表示 CPU0bit1 表示 CPU1以此类推。比如0x03代表允许在 CPU0 和 CPU1 上运行0x01则代表只允许在 CPU0 上运行。调度器每次做select_task_rq()选运行队列时会先用这个位图跟在线 CPU 取交集把“不允许跑的地方”直接剔除掉然后在剩余候选核心里选择目标。所以taskset设的掩码越窄调度器的可选空间就越小均衡性自然就更难保障——这是典型的“用均衡换局部性”的权衡。这里必须提醒一个实操中很常见的坑NUMA 架构下的跨节点访问。现在的服务器动不动就是双路 CPU两个 CPU 各自带着一批内存条CPU0 访问本节点内存很快访问 CPU1 的内存要跨 QPI/UPI 总线延迟能高三四倍。你要是把进程绑到了 CPU0但它malloc出来的内存大概率还在原来的 NUMA 节点上这就没问题但假如系统把内存分布打散了而你又把进程绑到了另一个节点性能反而会劣化。所以玩taskset前最好先看下numactl --hardware摸清节点布局。2.2 从内核角度看 taskset 的本质taskset只是个命令行包装器核心动作其实就一个系统调用设置亲和性时调用sched_setaffinity(pid, ...)查看亲和性时调用sched_getaffinity(pid, ...)这两个系统调用既可以传线程 IDTID也可以传进程 IDPID。传给 PID 的时候实际影响的是进程主线程的亲和性如果这个进程是多线程的其他线程并不会自动继承。这点很多人踩过坑用taskset -pc 2 主进程PID只绑住了主线程业务线程照样到处跑看起来绑了个寂寞。真正要绑多线程程序正确姿势是给进程内的每一个线程都设置一次或者干脆在程序内部用 pthread 接口调pthread_setaffinity_np()。另外子进程从父进程继承亲和性掩码但线程不是通过 fork 产生的所以不受继承规则保护——这也解释了为什么自测时taskset对单线程程序有效对线程池应用却表现诡异。你要想查看具体某个线程跑在哪个 CPU 上可以用ps -L -o pid,tid,psr,comm -p 进程PID。其中的psr列会直接告诉你当前线程正在哪个 CPU 上运行这是排查进程“到底绑没绑成功”最直观的命令。配上top -H -p 进程PID可以实时查看每个线程的 CPU 使用率两者结合基本能画出完整的调度画像。3. taskset指令完整用法速查3.1 基本语法和参数说明taskset的用法分两类启动新进程时绑定、对已运行的进程修改绑定。一个命令覆盖了两个场景简洁但容易搞混。启动新进程并绑核语法是taskset -c 2,3 ./my_service这行命令启动my_service并把它初始的亲和性设置为 CPU2 和 CPU3。查看或修改已运行进程的亲和性语法是taskset -pc 2,3 PID-c表示 CPU 列表模式后面可以跟单个编号-c 0逗号分隔的列表-c 0,2,4短线表示的范围-c 0-3混合写-c 0-3,7不加-c的话taskset接受的是十六进制掩码比如taskset -p 0x01 PID。实际工作中用-c可读性更好十六进制掩码在脚本里做复杂集合运算时才有价值。顺带一提查看当前进程的掩码只需要执行taskset -p PID输出会同时告诉你pid和mask的十六进制值。3.2 输出信息怎么读我贴一个真实输出示例$ taskset -p 11682 pid 11682s current affinity mask: f0xf二进制是1111意思是当前进程允许跑在 CPU0~CPU3 上。如果你的机器是 24 核看到ffffff之类的大掩码不要惊讶这说明进程没有被限制。想用更直观的方式看 CPU 列表就加个-c选项$ taskset -pc 11682 pid 11682s current affinity list: 0-3这里的输出就直白了允许范围是 0 到 3 号核心。从使用频率来看查看场景远多于修改场景。毕竟线上系统里大部分进程你根本不会动它的绑定关系只是想确认某个异常火龙CPU 拉满的进程到底在哪个核心上烧。加-c和不加的差异虽然小但建议养成统一使用-pc的习惯输出更自然、更好写进自动化巡检脚本里。实际上网上围绕“linux常用命令”的检索热度一直很高但很多教程列命令只给语法不给读法导致面试或者排障时一问就露馅——这里把读法掰开揉碎希望你能少走一次弯路。4. 实操查看、设置、验证三步走4.1 给一个正在运行的单线程进程绑核还是拿前文提到的my_service举例。假设它的 PID 是 11682我准备把它绑到 CPU2 和 CPU3 上。命令如下taskset -pc 2,3 11682执行后终端会输出pid 11682s current affinity list: 0-7 pid 11682s new affinity list: 2,3前一行是操作前的原始亲和性后一行是新的不用再去手动查证命令自己就帮我们对比了。这个设计还是挺贴心的回显里自带前后对照。但注意这只对进程的主线程生效。如果my_service内部还拉了一堆工作线程就得把这些线程的 TID 一个个找出来处理。查找线程 TID 用这个命令ps -T -p 11682会列出进程下所有线程的 TID 和名称。然后对每个需要绑定的 TID 分别执行一次taskset -pc 2,3 TID。嫌麻烦可以直接写成循环for tid in $(ps -T -p 11682 | awk NR1 {print $2}); do taskset -pc 2,3 $tid done注意awk NR1是跳过ps输出的表头行别把 ERROR 信息或者别的噪音也捞进去否则taskset会报invalid pid。生产环境脚本里建议加个数字校验再提交。但说实话线上多线程程序我一般不会用这种临时手动方式而是直接写进 systemd 单元文件下一节有完整配置或者应用代码里。手动绑线程适合临时压测、本地复现问题遇到重启或进程被拉起新线程后一切归零不适合长期使用。4.2 验证是否真正生效绑定完不等于万事大吉一定要用独立渠道验证调度结果。实际我见过太多人绑定后不看证据结果发现业务代码里又调用了sched_setaffinity(0, ...)把掩码改了白忙一场。验证方法第一步查看当前运行核心ps -o pid,psr,comm -p 11682其中psr列就是“当前运行或上次运行在哪个 CPU”。如果是多线程进程加上-L参数看每个线程各自的情况ps -L -o pid,tid,psr,comm -p 11682验证方法第二步压测时观察是否出现跨核迁移。你可以开三个终端一个跑mpstat -P ALL 1看各核心利用率一个跑ps -L -o tid,psr,pcpu -p PID定时采样另一个跑业务压测。如果绑核成功你会看到对应线程的psr一直稳定在 2/3mpstat里 CPU2 和 CPU3 明显忙碌而其他核空闲。这里还有个靠谱的小技巧绑核后通过/proc/PID/status里的Cpus_allowed_list字段能快速看到亲和性列表内容形如Cpus_allowed_list: 2-3这个文件和taskset -pc查出来的结果一致在脚本里 grep 它比调外部命令更轻量还避免了多次 fork 进程的开销。4.3 启动时绑核与常用组合操作不用先启动进程再修改启动这一刻就可以定好绑定关系taskset -c 2-5 ./my_service --configprod.yaml如果你希望通过nohup放后台运行可以把taskset和nohup组合nohup taskset -c 2-5 ./my_service /var/log/my_service.log 21 但这里有个实际经验要分享优先改用systemd而不是nohup。systemd通过CPUAffinity指令可以直接限制服务所在 cgroup 的可用 CPU 集合它的管理能力比nohup强得多日志收集、重启策略、依赖关系全都能统一管。比如写一个/etc/systemd/system/my_service.service[Unit] DescriptionMy CPU-Bound Service Afternetwork.target [Service] ExecStart/usr/local/bin/my_service CPUAffinity2-5 Restartalways RestartSec3 [Install] WantedBymulti-user.target保存后执行systemctl daemon-reload systemctl enable --now my_service这个服务就只会跑在 CPU2~CPU5 上。注意这里的CPUAffinity背后其实是 cpuset cgroup 的配置和taskset的直接系统调用原理不同但效果等价。区别在于cpuset 设置会影响该 cgroup 内后续 fork 的所有线程而taskset只是针对单个 PID 的一次性动作。生产环境里推荐用 systemd临时调试才用taskset这俩定位清晰不冲突。如果你在用 Docker 容器跑服务也别忘了容器层面有个天然限制方式docker run --cpuset-cpus2-5可以限制容器只能使用指定 CPU效果比进容器再绑核更干净因为容器内进程的sched_setaffinity无法逃出 cgroup 的限制范围。这些方案之间不是互斥的理解了原理后你可以按场景自由组合但它们共享一个底层资源——CPU 位图改错层级可能引发连带问题后面有常见问题专门讲。5. 进阶玩法numa、中断绑核与进程池5.1 把 taskset 和 NUMA 拓扑组合起来前文提到过盲目绑核可能把进程绑到“内存很远的核”。正确的做法是先把机器拓扑摸清楚。lscpu输出会显示每个 NUMA 节点的 CPU 列表。比如常见双路服务器输出长这样NUMA node0 CPU(s): 0-7,16-23 NUMA node1 CPU(s): 8-15,24-31这意味着 CPU0~7 和 16~23 物理上属于第一个 CPU 及其直连内存节点CPU8~15 和 24~31 属于第二个节点。绑定服务时如果你的进程主要在 node0 上分配内存最好也把它绑到 node0 的核上。命令示例taskset -pc 0-7,16-23 PID不过真到了 NUMA 场景单纯taskset其实不够精细。numactl --cpunodebind0 --membind0才是把“CPU 亲和”和“内存亲和”一起控制的组合拳。原因很简单你的进程即使绑在 node0 的核上如果不限制内存分配策略内核依然可能把内存页放在 node1结果绑核收益被跨节点访存完全抵消。我在一个内存数据库压测实例里实测过单独绑核延迟降低约 12%配合numactl --membind后延迟降低约 28%。差距非常大。所以这里有一条经验原则做 CPU 调优前必须先排摸 NUMA 布局不能只盯着taskset一个命令。对于线上规模较大的服务推荐直接用numactl或 cpuset cgroup 做隔离taskset更多留作快速查看和临时修改。5.2 网络中断绑核与 CPU 智能核心调度除了进程绑核中断IRQ也可以做亲和性设置。高并发网络服务中网卡收包中断如果频繁在不同 CPU 间跳跃会造成严重的锁竞争和 cache miss。网卡多队列开启后你可以把对应队列的中断分别绑定到不同核心上让每个核心处理固定的数据流。操作方法很简单echo 2 /proc/irq/45/smp_affinity_list其中 45 是中断号2 代表 CPU2。有些驱动还支持smp_affinity写十六进制掩码的旧写法。这里和taskset是同一套位图哲学区别只是操作对象从进程变成了中断。配合 RSSReceive Side Scaling或者 ethtool 的-L参数调整多队列能显著降低单核软中断压力这是大流量业务必备的调优手段。至于“CPU智能核心调度”这种话题多见于手机/桌面 CPU 的大小核心架构也就是 ARM 的 big.LITTLE 或 Intel 的 P-core/E-core 混搭。Linux 的schedutil和 EASEnergy-Aware Scheduling调度器会自动把负载往大核塞但这种调度的判断不一定符合业务预期。taskset在这种情况下依然可用——比如你明确知道某个批处理任务适合丢给能效核跑就可以直接把它绑到小核上避免抢占大核资源。不过桌面场景下更多还是用systemd或cpuset来做隔离因为用户态进程频繁 fork 子任务单点绑核意义不大。5.3 进程池应用与 Java 服务的绑核策略再讲讲后端常见的“进程池”场景。这类应用的特点是多 worker 进程或线程共享同一个监听 Socket典型的有 Nginx、PHP-FPM、Gunicorn以及各种 Java 线程池。对于这类应用绑核的关键是按照实例数量平均分配 CPU而不是把所有 worker 都塞到同一个核上。举个例子4 个 worker 进程跑在 8 核机器上给每个 worker 分 2 个核心是比较合理的起手式worker1 绑 CPU0-1worker2 绑 CPU2-3worker3 绑 CPU4-5worker4 绑 CPU6-7这样每个 worker 拥有独立的 L2 缓存线程来回切换范围被限制在相邻核心内cache 命中率最高。相反如果 4 个 worker 全绑在 CPU0-1 上它们共享同一对核心的计算单元和末级缓存不仅会互相抢资源还会因为调度竞争不断迁移性能反而比不绑还差。Java 服务绑核的注意点略不一样。JVM 的 GC 线程和业务线程混在一起运行如果直接把整个 JVM 进程绑到特定核心GC 的 STWStop The World停顿也会占用绑定的核心导致业务线程短暂饥饿。更细的做法是在 JVM 层配-XX:ActiveProcessorCount2让 JVM 的线程池感知允许的处理器数再配合taskset或者容器 CPU 限制做隔离。互联网上讨论“java进程 绑核”的典型结论基本都绕不开这两步控制 JVM 可见核心数再通过容器/cpuset 限制实际可达核心。还有一个容易被忽略的点绑定关系会随进程重启而丢失所以但凡线上要长期绑核必须写进启动配置里不能依赖事后手动操作。用容器就用--cpuset-cpus用 systemd 就用CPUAffinity裸跑进程可以把taskset写进启动脚本开头的exec行。这个“逢重启必失效”的问题我在生产环境踩了不止一次在这里先给你打预防针。6. 常见误区和故障排查实录6.1 绑定不生效的 4 种典型原因绑核后taskset -pc显示正确但psr列依然在多个核心之间跳这种情况在生产里太常见了。我把典型原因总结成一张排查表场景可能原因对策进程 PID 绑了但线程还在乱跑进程是多线程模型只绑了主线程对每个 TID 分别执行taskset -pc或在代码里调pthread_setaffinity_np绑定后进程启动新线程新线程不受控线程不继承父进程的 affinity只有 fork 的子进程才继承在初始化代码里统一设置线程亲和性别依赖命令行事后补救taskset提示 Operation not permitted进程归属其他用户或容器内有 seccomp/capability 限制确认运行用户权限检查容器 security profile必要时用sudo或调整容器配置容器内绑核失败/报无法设置cgroup cpuset 已经限制了容器可用 CPU二者冲突修改容器或 systemd 的 CPU 限制值统一从外层管理不要在内部再做一层互相打架的限制上面列表里容器内报“无法设置”其实是个比较隐蔽的场景。taskset成功执行的判断依据是sched_setaffinity()是否返回 0返回非 0 的时候可能错误码各不相同。常见的有EINVAL掩码里包含不存在的 CPU 编号、EPERM权限不够、ESRCH进程不存在。遇到这类报错先确认机器上到底有多少个 CPU用nproc查看再核对掩码范围避免白白消耗精力。另外强调一个“看起来无效”的大坑你给进程绑了核但进程本身只在一个核上运行又频繁睡眠唤醒psr在几个核之间小幅波动——这种其实不代表绑定失效因为内核允许在允许集合内的核心间做调度迁移只是它尽量少迁移。如果业务逻辑是“短睡短醒”型迁移依旧会发生。真想彻底锁死有两种路线一是只给亲和性掩码设置 1 个 CPU让内核别无选择二是用sched_setaffinity配合SCHED_FIFO这类实时调度策略把线程固定到核心后也设置实时优先级。6.2 绑核后性能反而变差的排查思路绑核不是“银弹”。我见过不少用户绑完核后压测数据不升反降然后一锤子定论“taskset 没用”。但真相往往是绑核放大了系统的局部瓶颈。常见的原因有几个第一绑核核心上有其他高负载进程在抢资源。你只把自己的服务绑过去了但没清理那个核心上原本就在跑的东西于是两伙负载互相挤压。第二绑核后中断和线程挤在同一核心。比如网卡中断默认落在 CPU0而你把业务进程也绑到了 CPU0软中断不跟你客气直接把 CPU0 打满业务线程只能排队。第三本进程是低延迟业务但它的内存和锁竞争不在绑核范围内。缓存局部性确实优化了但跨线程锁的 contention 成了新的痛性能瓶颈从 CPU 迁移到了互斥锁。排查这类问题我通常会分四步走先确认核心利用率分布用mpstat -P ALL 1看绑定的核心是否被其他进程挤占再查中断分布用cat /proc/interrupts看主要中断发生在哪些 CPU尤其是网络设备名的eth0-TxRx-*队列接着用perf或pidstat -t -p PID看用户态/内核态占比区分是业务逻辑变慢还是调度迁移变慢最后针对可疑点要么把中断挪走要么在绑核基础上扩展 1~2 个核心重新压测对比结果。这里说个实操心得绑核性能优化的第一步永远应该是“先验证不绑核时的基线”。先压测一轮记录 P99 延迟和吞吐再绑核压测同样的负载对比才能看出真实收益。只凭“感觉快了”就交付后面出问题根本没法归因。6.3 排查进程迁移与 CPU 抢占的现成命令如果你要快速定位一个进程这段时间有没有发生核心迁移可以用perf stat -e context-switches,cpu-migrations来观察。cpu-migrations事件数值如果居高不下就说明内核一直在把进程在不同的 CPU 间挪。这个数值配合taskset前后对比效果很好。另一个低成本的观察方式是通过/proc/PID/sched里面有se.nr_migrations字段记录该进程累计的迁移次数。绑核前后各看一次差值为 0 或者极少说明绑定生效并且内核没有在同集合内乱跳。注意允许集合为 1 个 CPU 时迁移次数应当严格为 0允许集合为多个 CPU 时迁移次数可能是非零的但这不代表绑定失效。再分享一个非常实用的“一键观察所有进程核心分布”的 aliasalias cpumapps -eo pid,psr,comm --sortpsr输入之后直接看到每个进程当前运行在哪个核心对快速定位“哪个核在烧”很有帮助。做压测时再配合watch -n 0.5 ps -L -o pid,tid,psr,pcpu -p PID就能看到线程在核心间的实时跳动情况。6.4 绑定关系与 cgroup/cpuset 打架时怎么办现代 Linux 系统里taskset并不是唯一能设置亲和性的机制。systemd、Docker、Kubernetes 都会通过 cpuset cgroup 来限制进程可用的 CPU。当你既在 cgroup 层设置cpuset.cpus又用taskset往里再钉一层的掩码最终生效的集合是二者的交集。举个例子cgroup 允许 0-7taskset设置 2-3进程实际只允许用 2-3但如果 cgroup 允许的是 4-7 和taskset设置 2-3交集为空进程调度就会很尴尬——内核会尝试寻找最合适但又在集合内的核心甚至可能返回错误导致进程无法运行。这也是生产容器场景最常踩的纸面陷阱。所以在云原生环境里最稳妥的做法是不要同时在两层设置 CPU 限制。选择 cgroup 作为唯一入口比如 Kubernetes 的cpu.cfs和静态绑核策略容器内部不要再用taskset做二次约束。反过来裸机部署服务用 systemd 的CPUAffinity或启动脚本里的taskset就不要在系统其他地方再叠加一层 cpuset。这种一致性原则比我前面提的任何具体命令都重要。7. 个人实操心得与延伸方向7.1 总结几条凭经验立住的绑核守则我给新手整理了一套相对稳妥的操作流程顺序错了容易绕弯路。第一步确认拓扑。先跑lscpu和numactl --hardware把 NUMA 节点、CPU 列表、超线程关系看清楚。同一个物理核上的两个逻辑 CPU一般编号挨着共享 L1/L2 缓存绑定逻辑 CPU 0 和 1 并不能拿到两个独立的核心性能这一点经常有人迷糊。第二步确认进程结构。用ps -L -o pid,tid,psr,comm -p PID看是单线程还是多线程。单线程直接绑多线程要决定是全局绑还是按线程绑必要时直接改代码调pthread_setaffinity_np。第三步以压测数据为准做前后对比。没有基线对比就没有发言权拿数据说话。第四步把绑定写进部署配置。临时调试可以命令行敲长期生效必须写 systemd 或容器编排配置里重启后自动恢复。最后补充一个容易被忽略的点超线程核心上绑定时要避免把两个高计算负载绑定到一对“兄弟核”上。用lscpu -e可以查看 CPU 和 core 的映射关系有逻辑 CPU 0 和 1 同属 core0那么高负载服务一个绑 CPU0、一个绑 CPU1 其实是在抢同一条执行流水线根本没利用到不同物理核的并行能力。正确做法是把它们分配到不同的 core 上。这也是为什么前面“进程池平均分配”里我会刻意建议把 worker 分到不相邻的核心组中。7.2 还能往哪些方向深入taskset只是 CPU 优化的敲门砖顺着这个话题还能摸出一整条性能优化技术栈。我简单列几个我知道的人都在深挖的方向你可以按自己的场景挑着看cpuset cgroup实现容器、systemd slice 的 CPU 隔离和taskset原理相通但层级更高irqbalance 与中断绑核处理高并发网络场景下的软中断分布配合网卡多队列使用NUMA 感知调度研究numad、numactl以及内核的NUMA balancing机制内存分配策略和线程调度联动优化实时调度与 SCHED_DEADLINE在延迟极度敏感的场景下用实时调度策略配合亲和性把抖动压到最低perf 与 pmu-tools用硬件计数器验证 cache miss 是否真的下降从数据层面确认绑核收益。这几块我都在实际项目中零碎用过各自都能单独撑起一篇文章。不过核心思想始终是统一的让该在哪儿干活的代码在哪儿干活减少无谓的奔波缩短关键路径上的等待。只要理解了 CPU 位图、调度器、缓存层级和 NUMA 这几块之间的咬合关系你看到的就不再是一个孤立命令而是一整幅性能和资源调度的全景图。我个人在实际使用过程中最大的体会是不要神化taskset也不要因为它是个老命令就轻视它。它的应用场景非常聚焦缓解跨核迁移导致的高速缓存失效、为性能关键服务锁定专属核心、配合 NUMA 策略控制内存访问距离。遇到任何“进程 CPU 飙升但不知道它在哪个核上瞎跑”的问题先跑一遍ps -o pid,psr,comm -p看分布再用taskset -cp做一次约束大概率能解决一半的困惑。剩下的就得交给系统链路里其他的调优工具了。
返回列表