首页 / 资讯中心 / 文章详情

中断延迟的 5 个来源:关中断、关抢占、软中断、IRQ 风暴、SMI 的逐项验证清单

中断延迟的 5 个来源:关中断、关抢占、软中断、IRQ 风暴、SMI 的逐项验证清单 ★ FEATURED ARTICLE
现场最常听到的一句话是抖动很大但不知道大在哪。延迟不是一个数字是五段路径叠加的结果。每一段有自己的成因、自己的观测手段、自己的判据。把它们分开问题就从一个玄学变成一个清单。前提本文基于 Linux 6.6。所有 tracer 与/proc、/sys接口均可对照Documentation/trace/与内核源码核对。文中不提供任何实测延迟数字涉及量级处标注为经验区间。本文的定位是怎么测不是测出来是多少。一、先把中断延迟拆开讨论延迟之前必须定义清楚测的是哪一段。否则两个人说的延迟根本不是同一件事。从硬件事件发生到一个处理它的线程真正开始执行中间经过硬件事件 ↓ ① 硬件传播与中断控制器ns 级通常可忽略 ↓ ② 当前 CPU 上正在执行的不可抢占代码 ← 关中断 / 关抢占 ↓ ③ 更高优先级中断的处理 ← 中断嵌套 ↓ ④ 软中断与 tasklet 积压 ← 软中断 ↓ ⑤ 线程就绪到被调度 ← 线程化后的调度延迟 处理代码开始执行外加两类不在路径上但会偷走时间的因素IRQ 风暴中断频率过高②③ 段被反复占用SMI / 固件CPU 被系统管理模式拿走操作系统完全不可见这就是五个来源的由来。注意它们不是并列的是串联的——总延迟是五段之和严格说是最大值叠加因为 ② 和 ⑤ 不会同时发生。来源典型量级经验区间可否在 OS 内消除关中断数 μs ~ 数百 μs可以缩短不能归零关抢占数 μs ~ 数十 μsRT 内核大部分已处理软中断积压数十 μs ~ 数 ms可以控制不能消除IRQ 风暴ms 级甚至更高可以靠修配置SMI / 固件数十 μs ~ 数百 μs不可以只能选硬件最后一行是关键SMI 决定了你能做到的下限。在有 SMI 的平台上追求个位数 μs 抖动是方向性错误。图1五段串联。②③ 段是关中断与中断嵌套④ 是软中断⑤ 是线程就绪到调度。二、来源一关中断2.1 机制local_irq_disable()/local_irq_save()之后本 CPU 不再响应任何可屏蔽中断。这段代码执行多久中断就被推迟多久。常见来源位置说明spin_lock_irqsave()自旋锁 关中断驱动里极常见驱动自己的local_irq_save()保护 per-CPU 数据硬中断处理本身进入中断时硬件自动关中断部分printk路径控制台锁可能关中断在通用内核上spin_lock_irqsave()在竞争时会自旋自旋期间中断是关的——这是最坏延迟的主要来源。在PREEMPT_RT上spinlock变成了rtmutexspin_lock_irqsave()不再真的关中断除非是raw_spinlock_t。这就是 RT 补丁最大的收益之一。2.2 怎么测内核自带irqsofftracer需要CONFIG_IRQSOFF_TRACERy多数发行版内核已开可用zcat /proc/config.gz | grep IRQSOFF确认。cd/sys/kernel/debug/tracing# 清空并停止echo0tracing_onechotrace# 选择 tracerechoirqsoffcurrent_tracer# 打开中断状态列输出里会显示 d/. 标记echo1options/irq-info# 开始记录echo1tracing_on跑一段时间让系统经历一次典型负载然后echo0tracing_oncattrace|head-60输出结构大致是# tracer: irqsoff # # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | idle-0 [002] d... 1234.567890: _raw_spin_lock_irqsave -foo_driver_ioctl idle-0 [002] d... 1234.567912: _raw_spin_unlock_irqrestore -foo_driver_ioctl idle-0 [002] d... 1234.567950: idle-0 22us : tracer: irqsoff关键信息d...标记d表示中断被关irqs-off.表示开22us这段关中断持续了 22 微秒调用栈_raw_spin_lock_irqsave - foo_driver_ioctl直接指出是谁干的2.3 判据与动作最大关中断时长经验区间判断动作 10 μs优秀无需处理10 ~ 50 μs可接受记录基线持续观察50 ~ 200 μs需要处理定位到具体驱动改锁粒度 200 μs严重该驱动不适合实时场景注意这是通用内核的参考区间。RT 内核上spin_lock_irqsave()已经不关中断了能测到长段关中断的地方通常只剩raw_spinlock、某些架构代码、以及IRQF_NO_THREAD的中断处理。2.4tracing_max_latency更省事如果不想看完整 trace直接看最大值cat/sys/kernel/debug/tracing/tracing_max_latency这个值会一直保留历史最大重启 tracer 才清零。适合做长时间监控# 每天早上看一次cat/sys/kernel/debug/tracing/tracing_max_latency# 清零echo0/sys/kernel/debug/tracing/tracing_max_latency三、来源二关抢占3.1 机制preempt_disable()之后调度器不会切走当前任务——即使有更高优先级的任务就绪。常见来源位置说明spin_lock()非 irq 版本通用内核里自旋期间禁抢占rcu_read_lock()部分配置下禁抢占get_cpu()/put_cpu()per-CPU 访问migrate_disable()RT 下用于替代部分禁抢占场景3.2 怎么测cd/sys/kernel/debug/tracingecho0tracing_onechotraceechopreemptoffcurrent_tracerecho1options/irq-infoecho1tracing_on需要CONFIG_PREEMPT_TRACERy。输出格式与irqsoff相同标记列里d变成.中断开着但preempt计数非零。3.3 想同时看两者echopreemptirqsoffcurrent_tracer这个 tracer 同时追踪关中断和关抢占输出里会标明是哪一类。排查时建议先用这个避免来回切换。3.4 RT 内核上这一项的特殊性RT 内核的spinlock已经是rtmutexspin_lock()不禁抢占也不关中断。所以在 RT 内核上跑preemptofftracer能测到的长段通常来自raw_spinlock_t的保护区间migrate_disable()区间RT 特有的只禁迁移不禁抢占架构相关代码如果 RT 内核上还能测到几十微秒的关抢占重点查raw_spinlock的使用是否过多。有些驱动为了绝对安全把该用spinlock的地方写成了raw_spinlock等于主动放弃了 RT 的收益。四、来源三软中断与 tasklet 积压4.1 机制软中断softirq在网络收包场景下最重。它的执行时机有两个中断退出时irq_exit()→invoke_softirq()在中断上下文里直接跑ksoftirqd线程积压超过预算时转给线程预算由两个参数控制cat/proc/sys/net/core/netdev_budget# 默认 300一次最多处理 300 个包cat/proc/sys/net/core/netdev_budget_usecs# 默认 2000最多花 2000 微秒含义中断退出时最多处理 300 个包或花 2000 微秒超了就唤醒ksoftirqd接着处理。这个机制本身就是延迟来源如果 300 个包 / 2000 微秒的预算被跑满同 CPU 上刚就绪的高优先级任务要等这段结束。4.2 怎么测/proc/softirqs是累计计数需要间隔采样求差# 第一次采样grep-ENET_RX|NET_TX|TIMER|RCU/proc/softirqs/tmp/softirq_1sleep5grep-ENET_RX|NET_TX|TIMER|RCU/proc/softirqs/tmp/softirq_2# 手动对比或写个脚本求差paste/tmp/softirq_1 /tmp/softirq_2|awk{ printf %-10s, $1; for (i2; iNF/2; i) printf %8d, $(iNF/2)-$i; print }输出示例每列对应一个 CPUNET_RX 1203 0 0 0 NET_TX 12 0 0 0 TIMER 980 976 981 978NET_RX在 CPU0 上 5 秒涨 1203 次其他核为 0——说明所有网络软中断都压在 CPU0。4.3 判据现象判断动作某个核的NET_RX增速远高于其他核中断或软中断集中检查中断亲和性、RPS 配置ksoftirqd/N占 CPU 高软中断积压已转线程降低netdev_budget或增加队列mpstat里%soft持续 5%软中断负载重分流或降载# 看每核的软中断占比mpstat-PALL15%soft列就是软中断占用。这个值在实时核上应该接近 0。4.4 一个反直觉的调整netdev_budget调小反而可能改善实时性echo64/proc/sys/net/core/netdev_budget原因预算小中断退出时很快让出 CPU剩下的交给ksoftirqd普通优先级线程可以被实时任务抢占。代价是吞吐下降、上下文切换增多。这是吞吐与延迟的经典取舍没有普适值取决于你的流量特征。五、来源四IRQ 风暴5.1 机制中断频率过高时CPU 大部分时间在进中断/出中断用户任务得不到执行。三种典型成因成因表现电平触发中断源未清中断一退出立刻再进计数暴涨共享中断上有设备异常每个中断都唤醒所有 handler全部返回IRQ_NONE设备故障或线缆问题中断持续产生无实际数据5.2 怎么测第一步算中断速率grepeth0 /proc/interruptssleep1grepeth0 /proc/interrupts两次计数之差就是每秒中断次数。正常网卡在满载时可能几万到几十万次/秒如果设备空闲却有几万次就是风暴。第二步看每个核的%himpstat-PALL15%hi是硬中断占用。某个核%hi持续 30% 以上基本可以确认风暴。第三步看 spurious 计数cat/proc/irq/47/spurious输出里的unhandled是未处理次数。如果它接近count说明大多数中断都没被认领。5.3 内核的自动保护note_interrupt()kernel/irq/spurious.c有一个兜底机制if(desc-irq_count99900desc-irqs_unhandled99900){printk(KERN_EMERGirq %d: nobody cared ...\n);desc-irq_data.chip-irq_disable(desc-irq_data);}十万次中断里几乎全没被处理内核直接把这个中断禁用并打印irq 47: nobody cared (try booting with the irqpoll option) Disabling IRQ #47看到这条说明中断已经被关了。设备会消失——这是排查设备突然不工作的重要线索。5.4irqpoll是什么要不要用irqpoll启动参数让内核在中断没被认领时轮询所有已注册的中断处理函数# /etc/default/grubGRUB_CMDLINE_LINUX... irqpoll它的定位是诊断工具不是生产配置。开启后能绕过共享中断认领失败的问题但每次未认领中断都要遍历全部 handler开销剧增实时性会显著变差正确做法是找到那个没认领的驱动修它而不是用irqpoll掩盖。5.5 一个容易忽略的场景MSI-X 网卡配了过多队列但实际流量只有一个流。结果是一个队列吃满其余队列空转中断分布极不均匀。grep-Eeth0-TxRx/proc/interrupts如果只有eth0-TxRx-0在涨其他不动说明 RSS 没生效单流场景下哈希只落一个队列。这时候减少队列数比强行分摊更有效。六、来源五SMI 与固件6.1 为什么单独拿出来前四个来源都在操作系统内可以观测、可以改。SMI 不行。SMISystem Management Interrupt是 x86 平台上的特殊中断由芯片组触发进入 SMMSystem Management Mode执行固件代码。整个过程操作系统完全不可见不能屏蔽NMI 都不能屏蔽它持续时间由固件决定几十到几百微秒不等期间 CPU 的所有计数器包括 TSC可能暂停或不同步这意味着你在 OS 内做的一切优化都无法消除 SMI 造成的延迟。6.2 怎么测hwlattracer需要CONFIG_HWLAT_TRACERy的原理很直接关中断然后紧循环读 TSC。如果两次读之间出现了远超预期的间隔说明 CPU 被偷走了——只可能是 SMI 或硬件层面的停顿。cd/sys/kernel/debug/tracing# hwlat 有独立参数目录lshwlat_detector/# width window# 设置检测窗口每 1 秒开一个 500 微秒的检测窗口echo1000000hwlat_detector/windowecho500000hwlat_detector/widthechohwlatcurrent_tracerecho1tracing_on跑几分钟后cattrace|head-30输出里会出现hwlat标记的记录带一个gap值...-1234 [002] d... 1234.567890: hwlat: hit latency: 82 us82 us就是这次被偷走的时间。图2横轴是时间纵轴是检测窗口内的实际间隔。正常点贴在底部出现 gap 的点直接跳到高位——这些尖峰就是 OS 不可见的时间。6.3 判据检测结果结论无 gap 记录没有 SMI 或很轻微偶发 gap 20 μs有 SMI但对多数场景可接受规律性 gap 50~200 μs如每秒一次典型的固件周期性任务温度采集、风扇控制gap 500 μs固件问题需要找板厂规律性是关键线索。如果 gap 出现的间隔非常稳定比如每 1.000 秒一次几乎可以确定是固件的周期性轮询。6.4 固件侧能不能改有些 BIOS 提供选项选项作用关闭 SMI 相关的电源管理减少周期性 SMI关闭 USB Legacy Support减少 USB 相关 SMI关闭串口重定向减少串口 SMI但工业主板上这些选项往往不可见或者关掉会影响功能。这是选硬件时就要考虑的事不是软件能补的。6.5 另外三个容易被归到硬件的因素这三个不属于 SMI但表现类似——OS 内可控但常被忽略① C-state 深睡眠退出延迟cat/sys/devices/system/cpu/cpu2/cpuidle/state*/latency数字是退出该 idle 状态需要的微秒数。C6/C7 可能上百微秒。实时核应该限制在 C1# /etc/default/grubGRUB_CMDLINE_LINUX... intel_idle.max_cstate1 processor.max_cstate1或者更激进GRUB_CMDLINE_LINUX... idlepollidlepoll让 CPU 空转不进 idle延迟最低但功耗和温度显著上升。只在隔离核上考虑整机用会烧板子。② CPU 频率切换频率从 800MHz 拉到 3GHz 需要时间期间执行速度按低频算。实时系统通常锁频cpupower frequency-set-gperformance③ 超线程兄弟干扰同一物理核的两个超线程共享执行单元。兄弟核上跑重负载会拖慢实时核。lscpu-e# 看 THREAD 与 CORE 的对应关系cat/sys/devices/system/cpu/cpu2/topology/thread_siblings_list# 输出 2,10 表示 cpu2 和 cpu10 是兄弟解决办法把实时核的兄弟核也隔离出来或者 BIOS 里关掉超线程。七、五类来源的判据矩阵把上面的内容压成一张表排障时直接对来源观测手段关键指标危险阈值经验可控性关中断irqsofftracertracing_max_latency 50 μs高改驱动锁关抢占preemptofftracer同上 50 μs高RT 已处理大半软中断/proc/softirqs采样单核增速、%soft%soft 5%中调预算/分流IRQ 风暴/proc/interrupts增速每秒中断数、unhandled 10 万/秒 或nobody cared高修配置SMI/固件hwlattracergap值与周期性 50 μs 且规律低换硬件图3五类来源的观测入口。前四项都能在 OS 内定位到具体函数SMI 只能确认存在。7.1 把五项做成一次体检每次排查都手敲五组命令太累。把只读部分合成一个脚本跑完直接给一张表#!/bin/bash# latency-scan.sh —— 中断延迟五源速查只读不改系统set-uopipefailecho 1. RT 生效性 echo-nrt_runtime_us: ;cat/proc/sys/kernel/sched_rt_runtime_usecho-nirq threads: ;ps-eLocomm|grep-c^irq/||echo0echoecho 2. 中断速率1 秒采样grep-vE^(NMI|LOC|RES|CAL|SPU|ERR|MIS|IWI|PMI)/proc/interrupts/tmp/irq_asleep1grep-vE^(NMI|LOC|RES|CAL|SPU|ERR|MIS|IWI|PMI)/proc/interrupts/tmp/irq_bpaste/tmp/irq_a /tmp/irq_b|awk-F\t{ a[$1]$0 }/dev/null# 简化版直接给每个中断一秒内的增量总和awkNRFNR{split($0,x,:); first[x[1]]$0; next} {split($0,y,:); if (y[1] in first) print y[1] : $0}/tmp/irq_a /tmp/irq_b\|head-5echoecho 3. 软中断分布5 秒采样grepNET_RX /proc/softirqssleep5grepNET_RX /proc/softirqsechoecho 4. 软中断占比 mpstat-PALL12|tail-20echoecho 5. 最长关中断/关抢占 if[-w/sys/kernel/debug/tracing/tracing_max_latency];thencat/sys/kernel/debug/tracing/tracing_max_latencyelseechodebugfs 未挂载或无权限fiechoecho 6. C-state 与超线程 forfin/sys/devices/system/cpu/cpu2/cpuidle/state*/latency;do[-f$f]echo$f:$(cat$f)donecat/sys/devices/system/cpu/cpu2/topology/thread_siblings_list2/dev/null这个脚本的价值不在输出多漂亮而在每次排查都走同一套口径。今天测的和上周测的能直接比才能判断改动有没有效果。注意第 2 段的中断速率统计要排除NMI、LOC、RES这些特殊行——它们的计数变化是内核 IPI与设备无关混进去会淹没真正的设备中断。八、排查顺序从最外层往里遇到延迟不达标按这个顺序走不要跳步图4前五步都是只读操作可以在生产环境直接做只有定位到具体驱动后才需要改代码。第 1 步先确认 RT 真的生效了这是上一篇的内容7 个检查点。如果ps里没有irq/线程、sched_rt_runtime_us还是 950000后面的排查全是浪费。第 2 步看有没有 IRQ 风暴grep-Eeth0|nvme/proc/interrupts;sleep1;grep-Eeth0|nvme/proc/interrupts mpstat-PALL13风暴是最容易发现的也是影响最大的。先排除它。第 3 步看软中断分布grepNET_RX /proc/softirqs;sleep5;grepNET_RX /proc/softirqs如果所有NET_RX都压在一个核而那个核正是实时核问题就找到了。第 4 步跑 preemptirqsoff 找最长阻塞段echopreemptirqsoff/sys/kernel/debug/tracing/current_tracer看tracing_max_latency如果超过 50 μs看 trace 里最长的那段是谁。第 5 步跑 hwlat 确认硬件层echohwlat/sys/kernel/debug/tracing/current_tracer有规律性 gap就是硬件/固件问题到此为止——OS 内已经没有优化空间。第 6 步最后才怀疑应用应用层的问题内存分配、缺页、GC、锁竞争通常表现为偶发但不是周期性与上面五类的特征不同。这个顺序的价值在于前 5 步都是只读的不会改变系统状态可以在生产环境上直接做。九、六个常见误判9.1 把 cyclictest 的最大值当成系统能力cyclictest报的是本次测试期间的最坏值它包含了一次偶然事件比如恰好撞上一次 SMI、一次日志刷盘。正确做法看分位数-h参数输出直方图关注 99.9% 而不是最大值。单次极值可能是噪声。9.2 只看平均值平均 5 μs、最大 500 μs 的系统对实时来说是失败的。实时系统的指标永远是最坏值或高分位数。9.3 测试期间跑着监控top、sar、node_exporter都会周期性唤醒产生额外中断和调度。测延迟时把它们停掉或者至少记录在案。# 测试前检查systemctl list-timers--allcrontab-l9.4 忘记锁频默认powersave调速器下CPU 频率随负载波动。同一个测试跑两次结果可能差 20%。测试前cpupower frequency-set-gperformance9.5 在有 SMI 的平台上追求个位数这是方向性错误。如果hwlat测出规律性 80 μs 的 gap那你的最坏延迟下界就是 80 μs做多少优化都突破不了。选平台时先测 hwlat再谈延迟指标。9.6 在有超线程的机器上不关兄弟核隔离了 CPU2但 CPU10 是它的兄弟。CPU10 上跑一个编译任务CPU2 的执行速度就下降。这是隔离了但还是抖的常见原因。9.7 抖动和漂移不是一回事这两个词在现场经常混用但它们指向完全不同的根因排查方向也相反。抖动jitter同一个周期任务相邻两次的实际执行间隔围绕目标值上下波动。目标 1000 μs998, 1004, 996, 1002, 1008, 993 ...特征是没有趋势围绕均值随机分布。根因通常是中断/软中断的随机抢占、SMI、缓存与内存争用、调度延迟。漂移drift实际间隔持续偏大或偏小误差不断累积。目标 1000 μs1000, 1003, 1006, 1009, 1012, 1015 ...特征是有单调趋势每次都比上一次晚一点。根因通常是时钟源精度、定时器实现方式CLOCK_MONOTONIC与CLOCK_REALTIME的选择、CPU 频率变化导致计时基准偏移、或者用了相对睡眠usleep而不是绝对时间clock_nanosleepTIMER_ABSTIME。区分方法把每次的实际间隔记下来做线性拟合。斜率接近 0 是抖动斜率明显非 0 是漂移。这个区分很重要抖动要去查五类延迟源漂移要去查时钟和定时器用法两者没有交集。用错方向查一周也查不出来。十、边界声明本文所有观测手段均为内核官方接口tracer 名称、/proc与/sys路径可对照Documentation/trace/与Documentation/admin-guide/kernel-parameters.txt核对。本文不含任何实测数据。所有经验区间如关中断 10 μs 优秀“gap 50 μs 需关注”来自公开资料与常见工程实践不同硬件平台差异可能达一个数量级。请以你自己平台上的测量结果为准。平台差异提示hwlattracer 在虚拟机上通常无法反映真实 SMI宿主的 SMI 对 guest 不可见ARM64 平台没有 x86 意义上的 SMI但有类似的固件介入PSCI、secure monitor表现和排查方式不同irqsofftracer 在部分发行版内核上未编译进去需要自己确认CONFIG_IRQSOFF_TRACER。最后一句先把五段拆开再谈优化。一个笼统的抖动 200 μs没法处理拆成关中断 15 μs 软中断 80 μs SMI 100 μs每一段都有明确的下一步。
阅读完成 · 觉得有帮助?
咨询建站