标签: CFS调度器

  • 深入 CPU 亲和性陷阱排查:HT 物理核争抢引发的 L1 Cache 颠簸与 CFS 性能退化实战

    盲目使用 taskset 绑定 CPU 亲和性,若无视底层的 NUMA 与超线程(HT)拓扑,极易引发性能灾难。核心结论:将高负载线程绑定到同一物理核的两个逻辑核上,会导致 L1/L2 Cache 严重颠簸与 ALU 运算器争抢,使进程 IPC 大幅下降。由于强绑定限制,CFS 调度器无法进行跨核负载均衡。解决方案:解析 /sys/devices/system/cpu/ 拓扑树,将延迟敏感任务绑定到独立物理核,并配合 isolcpus 剔除内核干扰。

    现场还原:P99 飙升与离奇的 CPU 闲置

    排查过程中遇到一个经典场景:某个自研的高性能 C++ 网关应用(基于 epoll 跑在 Kernel 5.4.x 环境),为追求极低延迟,研发人员使用了严格的 taskset -cp 将 4 个 Worker 线程绑定到 CPU 8, 9, 10, 11 上。

    测试环境表现尚可,但一到生产环境面临 10万+ QPS 的真实流量时,监控面板的 P99 延迟直接从 2ms 飙升到了 35ms,甚至偶发超时熔断。

    奇怪的是,查看系统级指标:

    • Load Average 并不高。

    • mpstat -P ALL 1 显示这几个绑定的 CPU 使用率只有 60% 左右。

    • 没有明显的 I/O Wait。

    如果 CPU 没跑满,为什么会出现严重的延迟毛刺?

    抽丝剥茧:揪出 Cache 颠簸元凶

    遇到这种“CPU 没跑满但延迟极高”的 CPU-Bound 任务,直觉告诉我,要么是自旋锁争抢,要么是 CPU 缓存或者流水线出了问题。祭出 perf 查看微架构指标。

    对其中一个 Worker 线程(PID 10245)抓取指令周期与 Cache 命中率:

    # 采样 10 秒钟的核心硬件指标
    perf stat -p 10245 -e cycles,instructions,cache-misses,L1-dcache-load-misses,L1-icache-load-misses sleep 10
    

    输出结果非常扎眼:

     Performance counter stats for process id '10245':
    
         3,456,123,908      cycles                    #    3.2 GHz
         1,210,034,211      instructions              #    0.35  insn per cycle (IPC)
           215,892,103      cache-misses              #    ...
         1,890,210,345      L1-dcache-load-misses     #    38.4% of all L1-dcache hits
    
          10.001234123 seconds time elapsed
    

    IPC (Instructions Per Cycle) 仅为 0.35。正常优化良好的 C++ 网络服务,IPC 至少应该在 1.0 以上。L1 数据缓存未命中率高达 38.4%,说明流水线大部分时间都在 stall(停顿),等待主存数据。

    为什么 Cache 会颠簸得如此厉害?查看绑定的 CPU 拓扑。

    lscpu -p=CPU,CORE,SOCKET,NODE | grep -v '#'
    # 节选输出:
    ...
    8,4,0,0
    9,5,0,0
    10,4,0,0
    11,5,0,0
    ...
    

    破案了。研发绑定的 CPU 8, 9, 10, 11,对应的物理核(CORE)其实是 45。 具体来说,逻辑核 8 和 10 是同一个物理核 4 的超线程(HT)对;逻辑核 9 和 11 是物理核 5 的超线程对。

    为什么 HT 伪共享会击穿 CFS 调度器预期?

    在 x86 架构中,SMT(超线程)技术让一个物理核虚拟出两个逻辑核,但这两个逻辑核是共享 L1/L2 Cache 以及 ALU(算术逻辑单元)的

    当两个 CPU 密集型的高并发线程被硬绑定在 CPU 8 和 CPU 10 上时:

    1. 硬件层面的踩踏:线程 A 刚把热点数据加载进 L1 Cache,线程 B 运转时瞬间将其驱逐(Eviction)。两个线程在同一个物理核内发生了剧烈的 Cache 争抢(Cache Thrashing)。ALU 流水线也被塞满,导致计算能力退化,IPC 断崖式下跌。

    2. CFS 调度器的无力:Linux CFS(完全公平调度器)在构建调度域(sched_domain)时,知道 8 和 10 属于 SD_SHARE_CPUCAPACITY(共享 CPU 容量)。正常情况下,CFS 会尽量把任务打散到不同的物理核上。但是,研发使用了 taskset(底层是 sched_setaffinity 系统调用)进行了强制 cpumask 绑定。 CFS 的负载均衡器(Load Balancer)被严格的亲和性规则限制,无法将任务迁移到系统其他空闲的物理核上。

    这两个线程被死死按在同一个物理核上互相绞杀,单核的真实算力从 1.0 骤降到 0.6,这就是为什么 mpstat 看起来只跑了 60%,但延迟已经完全崩坏的原因——不是算力没用满,而是算力上限暴跌了

    防御性隔离方案:从内核到 K8s 的落地

    要彻底解决由于 CPU 亲和性设置不当带来的延迟问题,必须做到“拓扑感知”。

    1. 脚本化解析独立物理核

    在宿主机部署服务时,禁止硬编码绑核。可以使用如下 Bash 脚本获取处于同一 NUMA 节点且各自独占物理核的逻辑 CPU 列表:

    #!/bin/bash
    # 找出 NUMA Node 0 上的独立物理核对应的第一个逻辑核
    numa_node=0
    cpus=$(lscpu -p=CPU,CORE,NODE | grep -v '#' | awk -v node="$numa_node" -F, '$3==node {print $0}')
    
    declare -A seen_cores
    result=()
    
    for line in $cpus; do
        cpu=$(echo $line | cut -d, -f1)
        core=$(echo $line | cut -d, -f2)
        if [[ -z "${seen_cores[$core]}" ]]; then
            seen_cores[$core]=1
            result+=($cpu)
        fi
    done
    
    echo "可绑定的独立逻辑核: ${result[*]}"
    # 输出示例: 可绑定的独立逻辑核: 0 1 2 3 4 5 6 7
    

    将 Worker 线程绑定到这批 CPU 上,杜绝 HT 资源内耗。

    2. K8s 环境下的 CPU Manager 策略

    如果业务运行在 Kubernetes(例如 v1.22+)中,千万不要在容器的 entrypoint 脚本里自己玩 taskset。正确的做法是利用 Kubelet 的 CPUManager

    /var/lib/kubelet/config.yaml 中配置:

    cpuManagerPolicy: static
    topologyManagerPolicy: single-numa-node
    

    并在 Pod 的 YAML 中设置 Guaranteed QoS(requests 和 limits 相等且为整数):

    resources:
      requests:
        cpu: "4"
        memory: "8Gi"
      limits:
        cpu: "4"
        memory: "8Gi"
    

    Kubelet 会自动分配独占的物理核,底层利用 cgroup 的 cpuset 子系统进行严格隔离,并且优先分配属于同一个 NUMA Node 且完整的物理核,避免跨 NUMA 内存访问延迟和 HT 争抢。

    3. 终极隔离:Kernel 启动参数调优

    对于极高要求的 DPDK 或核心网关进程,仅靠用户态的 cpuset 还不够。内核的软中断(ksoftirqd)、RCU 回调、定时器滴答(tick)依然会打断当前 CPU。 需要在 Grub 启动参数中加入:

    isolcpus=4-7 nohz_full=4-7 rcu_nocbs=4-7
    
    • isolcpus:将 CPU 4-7 从 CFS 的普通调度队列中剔除。

    • nohz_full:在 CPU 只有一个可运行任务时,关闭时钟滴答,消除 Context Switch 开销。

    • rcu_nocbs:将 RCU 回调转移到其他 CPU 执行。

    配置后,通过 taskset -c 4-7 ./gateway_app 启动程序,即可获得近乎物理机裸金属级别的极致低延迟。

    常见问题 (Q&A)

    Q1:我是不是直接进 BIOS 把 Hyper-Threading(超线程)关掉就一劳永逸了? 视业务场景而定。对于普通的 Web 服务、微服务集群,HT 能够提升整个系统 20%~30% 的并发吞吐量;但对于 Redis、DPDK 这种重度依赖 L1/L2 Cache 和极低延迟的 CPU-Bound 任务,HT 是毒药。如果没有混合部署的需求,纯跑高频核心服务,关闭 HT 是最简单的防误操作方案。

    Q2:使用了 isolcpus 之后,发现绑定的 CPU 依然偶尔会处理网络中断,为什么? isolcpus 只是隔离了进程调度,并没有隔离硬件中断(IRQ)。你还需要配置 SMP IRQ Affinity。修改 /proc/irq/default_smp_affinity,或者使用 irqbalance 黑名单机制,把网卡的软硬中断绑定到非 isolcpus 的核上(比如 CPU 0-3),否则高频网卡中断依然会击穿你的隔离。

    Q3:taskset 和 cgroup 的 cpuset 有什么本质区别? taskset 是基于线程级别的亲和性掩码(Bitmask)设置,CFS 调度时会取任务掩码与系统可用 CPU 的交集,比较轻量,但容易被后续的系统调用覆盖。而 cpuset 属于 Cgroup 树形资源控制,不仅能限制进程组使用的 CPU,还能限制内存 NUMA 节点分配(cpuset.mems),隔离性更强。在容器化场景下,绝对推荐使用 cpuset,废弃 taskset

  • 深入 CFS 调度陷阱排查:sched_autogroup_enabled 引发的线程池饥饿与 P99 延迟毛刺实战

    某次核心网关集群的性能抖动排查,生动诠释了什么叫“服务器瞎开桌面级优化,全链路跟着火葬场”。

    故障背景与最终结论: 一个承担 3 万 QPS 的 Go 编写的 API 网关服务(部署在 32C128G 的物理机上),在特定时间段 P99 延迟会毫无征兆地从 2ms 暴涨到 300ms 以上,甚至引发下游微服务的限流与重试风暴。诡异的是,监控面板上的 CPU 使用率仅在 60% 左右波动,系统 Load Average 也没有超过 CPU 核数,网络和磁盘 IO 更是平稳得像一条直线。 最终结论: 罪魁祸首是 Linux 内核的 sched_autogroup_enabled 特性(在部分发行版中默认开启)。一个定时执行的单线程本地日志打包脚本(tar -czf),触发了 CFS(完全公平调度器)的 Autogroup 逻辑,导致拥有数百个 Goroutine 线程的网关进程,与单线程的 tar 进程获得了同等权重的 CPU 时间片,进而引发网关线程池出现大面积的 CPU 调度饥饿(Runqueue Wait)。 一秒解决: sysctl -w kernel.sched_autogroup_enabled=0

    案发现场:被误导的排查方向

    刚接手这个问题时,业务侧和网络团队已经拉扯了几天。开发看监控觉得 CPU 没打满,坚称是宿主机网络丢包;网络侧拿 tcpdump 抓包证明是应用侧处理慢,ACK 回得迟。

    我切到线上机器,直接抓了一波抖动期间的现场。既然资源使用率没到瓶颈,但延迟飙升,第一反应必须是调度延迟(Scheduling Latency)。 直接掏出 perf 看看 CPU 在等什么:

    # 采样10秒的调度事件
    perf sched record -- sleep 10
    # 分析调度延迟
    perf sched latency --sort max
    

    结果一出来,极其刺眼:

     ---------------------------------------------------------------------------------------
      Task                  |   Runtime ms  | Switches | Average delay ms | Maximum delay ms |
     ---------------------------------------------------------------------------------------
      gateway-api:29314     |   1234.567 ms |    8432  |      18.453 ms   |     245.672 ms   |
      gateway-api:29315     |   1210.123 ms |    8110  |      19.123 ms   |     238.102 ms   |
      tar:4102              |    890.334 ms |     150  |       0.012 ms   |       0.105 ms   |
     ---------------------------------------------------------------------------------------
    

    网关进程(gateway-api)的线程,最大调度延迟(Maximum delay)竟然飙到了 245ms!这意味着一个就绪的线程在 Runqueue 里干等了四分之一秒才被分配到 CPU 执行。而那个跑在后台的 tar 进程,平均延迟只有 0.012ms,几乎是想什么时候跑就什么时候跑。

    为什么拥有几百个活跃线程的重负载核心进程,会被一个单线程的普通打包脚本“欺负”成这样?

    底层原理:当服务器内核操着桌面系统的心

    这就不得不提 Linux 内核历史上的一个著名补丁——2010 年内核开发者 Mike Galbraith 提交的针对桌面环境优化的补丁,即后来的 sched_autogroup_enabled 特性。

    在默认的 CFS(Completely Fair Scheduler)中,调度的基本单位是 Task(线程/进程)。如果没有 Autogroup,500 个网关线程和 1 个 tar 线程,总共 501 个 Task,CFS 会大致均分 CPU 时间,网关进程拿到 500/501 的算力,tar 拿到 1/501。这在服务器上非常合理。

    但开启 sched_autogroup_enabled=1 后,游戏规则变了。 为了提升桌面用户的交互体验(比如你在开着几十个线程编译内核的同时,依然能流畅地拖动浏览器窗口),内核会自动按 Session ID (SID) 或 TTY 将进程分组(Task Group)。CFS 会先在 Autogroup 之间公平分配 CPU 时间,然后再在组内的 Task 之间分配。

    你可以通过这个命令看看系统里的 Autogroup 状态:

    cat /proc/sched_debug | grep -A 5 "cfs_rq"
    

    在案发机器上,因为网关服务是通过 Systemd 启动的,属于一个 Session;而定时任务 Crontab 拉起的 tar 脚本属于另一个 Session。 于是,内核调度器做出了极其荒谬的“公平”裁决:

    • Group A(网关进程及 500 个线程):获得 50% CPU 时间份额。

    • Group B(tar 进程及 1 个线程):获得 50% CPU 时间份额。

    这也就是为什么在整体 CPU 使用率只有 60% 的情况下,tar 进程吃满了它能吃到的单核资源,而网关的 500 个线程却在一个缩水的 CPU 时间池里疯狂争抢排队,导致 vruntime(虚拟运行时间)失衡,Runqueue 等待时间剧增,宏观表现就是 P99 延迟呈现断崖式恶化。

    致命连环:为什么不可原谅?

    这是一个极其低级但隐蔽的运维坑。之所以说它不可原谅,是因为它违背了服务端架构的基础防线:

    1. 基础环境未做服务端标准化收敛:CentOS 7/8 和部分 Ubuntu 版本默认是开启这个参数的。把带有强烈桌面属性的调度策略原封不动搬到高并发服务器上,暴露出系统初始化压根没有经过内核层面的 Tuning 审核。

    2. 后台任务缺乏资源隔离(Cgroup):在宿主机上跑定时脚本,居然不套 nice,也不做 systemd-run 的 CPUQuota 限制。一个不受控的后台脚本能撼动核心网关的调度,这种“裸奔”操作在生产环境是灾难性的。

    破局与防御性配置

    弄清楚了原理,修复就是敲几下键盘的事,但更重要的是后续的防御性加固。

    1. 全局禁用 Autogroup 直接在 sysctl.conf 中封杀此特性,服务器不需要这种“桌面级关怀”:

    echo "kernel.sched_autogroup_enabled = 0" >> /etc/sysctl.d/99-sysctl.conf
    sysctl -p /etc/sysctl.d/99-sysctl.conf
    

    执行完毕后,网关的 P99 延迟瞬间回落到 2ms 的常态平滑线。

    2. 剥夺后台脚本的调度特权 所有非核心业务的运维脚本,严禁直接丢进 Crontab。必须通过 systemd timer 触发,并强制声明 CPU 和 IO 优先级:

    # /etc/systemd/system/log-backup.service
    [Service]
    Type=oneshot
    ExecStart=/opt/scripts/backup.sh
    # 限制只能使用单核的20%
    CPUQuota=20%
    # 降低调度优先级 (默认0,19为最低)
    Nice=19
    # 降低 IO 优先级
    IOSchedulingClass=idle
    

    3. 监控维度的升维 不要只盯着 node_cpu_seconds_total(CPU 使用率)。CPU 没打满不代表 CPU 资源不紧张。必须将调度队列长度纳入核心监控指标。 利用 eBPF 工具集(BCC)的 runqlen 或在 Prometheus 侧采集 /proc/stat 中的 procs_running 状态,一旦发现 Runqueue 长度持续大于物理核数,立即触发告警。

    同类问题速查清单 (Troubleshooting Checklist)

    1. 检查 Autogroup 开关状态:通过 sysctl kernel.sched_autogroup_enabled 确认是否开启,服务端强烈建议设为 0

    2. 审查进程所属组:通过 cat /proc//autogroup 查看目标进程是否被错误降权分配到非预期的分组中。

    3. 排查微观调度延迟:使用 perf sched latency 或 BCC 工具集 runqlat.py,定位是否存在 CPU 占用率低但就绪队列等待时间过长的现象。

    4. 清理僵尸 Crontab 与后台脚本:排查宿主机是否有定时触发的重 CPU/IO 操作(如 gzip, tar, find),强制要求套用 nice -n 19 或转入受限的 cgroup 运行。

    5. 核对 systemd cgroup 策略:确认是否开启了 DefaultCPUAccounting=yes 导致 systemd 按 service 粒度强行隔离了 CPU 份额,避免多线程重负载服务被意外限流。

  • 深入 CFS 调度器陷阱排查:cgroup quota 微突发引发的无辜节流与 P99 延迟雪崩实战

    在 K8s 容器环境中,CPU 使用率不到 20% 却频繁出现 P99 延迟毛刺,根本原因是 CFS 带宽控制(cfs_quota_us)在微突发场景下的过度节流(Throttling)。解决方案:要么升级内核至 5.14+ 开启 CPU Burst 特性,要么对核心时延敏感型服务启用 Kubelet static CPU Manager 策略以独占物理核并绕过 quota 限制,辅以 NUMA 节点绑定。

    排查过程中,我们遇到一个典型且极其隐蔽的性能陷阱:一个用 Go 编写的高并发 API 服务,Pod 配置为 requests: 4, limits: 4,Prometheus 监控显示其 CPU 利用率峰值从未超过 1.5 核。然而,业务端频繁报出超时,网关层统计的 P99 延迟从平稳的 20ms 间歇性飙升至 300ms 以上。

    没有 GC 停顿,网络抓包无丢包,存储 IO 处于极低水位。唯一的异常落在 cgroup 的 CPU 调度统计上。

    执行以下命令查看该 Pod 对应容器的底层调度指标:

    # 进入容器对应的 cgroup 目录 (路径依 Cgroup v1/v2 及容器运行时有所不同)
    cat /sys/fs/cgroup/cpu/kubepods.slice/kubepods-pod<uid>.slice/docker-<cid>.scope/cpu.stat
    

    输出令人吃惊:

    nr_periods 542100
    nr_throttled 184520
    throttled_time 4851230000000
    

    超过 34% 的调度周期(nr_throttled / nr_periods)发生了节流(Throttling),累计被限制运行的时间高达 4851 秒。容器被系统强制“按下了暂停键”。

    为什么 CPU 使用率极低也会触发 CFS 节流(Throttling)?

    要理解这个诡异现象,必须剖析 Linux 内核完全公平调度器(CFS)的带宽控制(Bandwidth Control)机制。

    在 Kubernetes 中,设置 CPU limits 本质上是在配置 cgroup 的 cpu.cfs_period_uscpu.cfs_quota_us。 默认情况下:

    • cpu.cfs_period_us = 100000(100 毫秒),即调度周期。

    • 对于 limits: 4cpu.cfs_quota_us = 400000(400 毫秒)。

    “微突发(Micro-burst)”导致的无辜节流: Go 程序的协程(Goroutine)极多。假设在某个 100ms 的调度周期初始,网关突然打来一波并发请求,Go runtime 唤醒了 16 个 OS 线程来处理。 这 16 个线程在多核宿主机上并行执行,虽然每个线程仅仅执行了 25ms,但总计消耗的 CPU 时间为 16 * 25ms = 400ms

    此时,距离当前 100ms 周期结束还有 100ms - 25ms = 75ms,但 400ms 的 quota 已经被瞬间耗尽。 CFS 调度器的直接反应是:强制剥夺该 cgroup 内所有线程的执行权,挂起等待下一个 100ms 周期。 这就导致了业务请求在这 75ms 内得不到任何 CPU 资源,直接反映为 P99 延迟无端增加 70~80ms,且多次叠加后引发雪崩。而在更高维度的 Prometheus 监控中(通常是 15s 或 1m 抓取一次),这种 100ms 级别内的剧烈波动被彻底抹平了,导致 CPU 使用率看起来极其“健康”。

    破局方案与底层调优实战

    为了彻底解决 CFS 调度导致的延迟毛刺,我们分层级实施了以下架构改造,拒绝简单的“无脑放大 limits”。

    1. 终极解法:内核 CPU Burst 特性 (Kernel >= 5.14)

    在较新的内核版本中(部分大厂针对 Kernel 4.19/5.4 已 backport 该特性),内核引入了 cpu.cfs_burst_us。它允许容器将历史周期内未用完的 quota 累积起来,应对突发流量。 通过向容器注入类似配置,允许最大爆发额度(比如额外允许 400ms):

    echo 400000 > /sys/fs/cgroup/cpu/kubepods.slice/.../cpu.cfs_burst_us
    

    这一机制类似于令牌桶算法,有效吸收了微突发流量。开启后,nr_throttled 归零,P99 延迟恢复平滑。

    2. K8S 侧解法:启用 CPU Manager 的 Static 策略

    如果内核版本较低(如 CentOS 7 的 3.10 或标准 Ubuntu 20.04 的 5.4),我们必须规避 CFS quota。手段是通过 Kubelet 的 CPU Manager 将容器进程与物理 CPU 进行绑核(cpuset),并移除 cgroup quota 限制。

    修改 kubelet 配置文件 /var/lib/kubelet/config.yaml

    cpuManagerPolicy: static
    topologyManagerPolicy: single-numa-node
    

    Pod 配置规范: 必须保证 QoS 为 Guaranteed,即 requests 必须等于 limits,且值为整数。

    resources:
      requests:
        cpu: "4"
        memory: "8Gi"
      limits:
        cpu: "4"
        memory: "8Gi"
    

    此时 Kubelet 会通过 cgroup 的 cpuset.cpus 分配 4 个独占的逻辑核(例如 4-7),由于是独占,底层不再依赖 cfs_quota_us 限制,从而彻底根除 Throttling。

    3. 极客进阶:防御中断风暴与 NUMA 错位

    仅仅使用 cpuset 绑核并不完美。即使应用独占了 CPU 4-7,依然可能被网卡中断(Hard IRQ)和软中断(Softirq)抢占。通过 perf schedmpstat 可以看到上下文切换(CS)依然很高。

    隔离内核调度(Isolcpus 配合 IRQ Affinity): 修改宿主机 Grub 内核启动参数,将部分 CPU 从内核默认调度域中剔除:

    GRUB_CMDLINE_LINUX="... isolcpus=4-7 nohz_full=4-7 rcu_nocbs=4-7"
    

    同时调整中断亲和性,避免网卡队列中断落到被隔离的核上:

    # 将中断限制在 0-3 核上处理
    for irq in $(ls /proc/irq/); do
        echo 0-3 > /proc/irq/$irq/smp_affinity_list 2>/dev/null
    done
    

    这就为高吞吐、低延迟的核心业务打造了一条纯粹的“物理超车道”。

    常见问题 (FAQ)

    Q1:K8s 中设置 requests == limits 会带来什么调度层面的影响? A:除了触发 Guaranteed QoS 避免 OOM 驱逐外,在 CPU 调度层面,如果不开启 Kubelet CPU Manager static 策略,它依然受制于 CFS Quota 限制。只有在 static 策略下,整核的 requests==limits 才会触发底层 cpuset 独占逻辑,从而完全绕开 CFS 周期结算,这对时延敏感型(Latency-sensitive)应用至关重要。

    Q2:绑核(Taskset/cpuset)后,为什么还会出现 CPU 缓存未命中(Cache Miss)飙升? A:通常是因为 NUMA 节点未对齐。如果分配的 CPU 核在 NUMA Node 0,但进程访问的内存被分配在 NUMA Node 1,跨 QPI/UPI 总线访问内存会导致严重的延迟。解决方案是在 Kubelet 开启 topologyManagerPolicy: single-numa-node,强制 CPU 和内存在同一 NUMA 节点内分配。

    Q3:内核参数 kernel.sched_min_granularity_ns 对高并发有什么直接作用? A:该参数定义了 CFS 调度中一个任务在被抢占前能够保证运行的最小时间片段(默认一般为 3ms-10ms)。在极其密集的上下文切换场景(如成千上万个轻量级连接),适当调大该值可以减少上下文切换带来的开销,提升系统总吞吐量(Throughput),但代价是牺牲了一定的调度响应时延(Latency)。调整时需通过 perf 工具严格评估收益。

    Q4:为什么在高并发数据库(如 MySQL/Redis)的宿主机上,极不推荐混部 RT(实时)调度策略的进程? A:RT 任务(如 SCHED_FIFO / SCHED_RR)优先级高于所有的 CFS 普通任务。如果一个存在死循环或长时间未主动 yield 的 RT 进程跑满单核,不仅会饿死同核上的 MySQL 工作线程,甚至可能导致内核态的软狗(Soft Lockup)超时引发内核 panic。控制 RT 任务的爆炸半径,必须严格配置 kernel.sched_rt_runtime_uskernel.sched_rt_period_us