中断不控,延迟难稳:低延迟系统的终极痛点

55 阅读9分钟

一、引言

在金融交易、工业控制、高性能网络等要求微秒级时延的系统中,“中断”常常是影响尾延迟(Tail Latency)的关键因素之一。 在普通系统里,中断带来的几十微秒抖动并不算问题,但在低延迟系统中,即使是 几微秒的不可控波动,都可能导致性能抖动、订单错失。 本文将从原理、影响、排查等维度,系统性介绍中断对低延迟系统的影响。

二、低延迟系统的特点

低延迟系统的目标是在极短时间内完成处理,并保持响应时间高度稳定。这类系统通常有以下共性,使得它们对中断格外敏感:

1. 强调确定性

低延迟系统不仅追求“快”,更追求“稳定”。它希望每一次响应时间都尽量一致,不出现无规律的波动。而中断的触发往往具有不确定性,会破坏这种稳定性。

2. 核心资源为业务专用

为了减少干扰,低延迟系统通常会预留部分 CPU 只运行核心业务逻辑。
任何外来的系统任务、中断或调度行为进入这些核心,都会破坏业务线程的专注度,引发延迟尖刺。

3. 对尾延迟特别敏感

很多系统关注平均延迟,但低延迟系统还关注最差情况(例如 99.9%、99.99% 延迟)。
因此哪怕偶尔一次中断引发几十微秒的延迟,也会被放大成系统性能问题。

4. 对干扰高度敏感

低延迟系统通常关闭节能、规避抢占、避免内核后台任务等。
在这种极限优化状态下,即使是非常小的系统活动,例如一次定时器中断,也可能打断业务流程,从而产生可观的抖动。

三、中断如何影响低延迟系统?

中断在低延迟系统中的影响主要体现在三个层面:

1. 抢占执行、打断业务线程

中断会中止正在运行的业务代码,引起:

  • CPU L1/L2 缓存污染
  • pipeline flush
  • TLB miss 增加
  • 业务线程指令乱序恢复

对于微秒级任务,这种打断是“毁灭性”的。

2. 导致不可预测的时延尖刺

中断不连续、不规则,例如:

  • 某个 IRQ 在负载高时突然 flood
  • 某个 housekeeping 定时器每几秒被触发
  • 某个软中断在 backlog 到阈值后被 forced 执行

这些都会导致业务端出现 几微秒到几十微秒的波动。

3. 软中断(SoftIRQ)更隐蔽

很多系统管理员只关注“硬中断”,但真正杀伤力高的是:

  • NET_RX
  • NET_TX
  • TIMER
  • TASKLET
  • RCU

它们会在本 CPU 上被强制执行,且执行时长不可控,极容易破坏延迟稳定性。

四、如何排查中断对延迟的影响?

在低延迟系统中,中断是否落到了业务核心、是否存在异常增长,是判断延迟抖动来源的关键依据。
传统方式通常是直接 cat /proc/interrupts,但它不具备实时性,也难以观察变化趋势。
因此,使用自研的 irq_watch.py 脚本,实现实时监控中断变化,帮助快速定位问题。

1. irq_watch.py 的功能概述

该脚本的核心作用是:

  • 实时监控指定 IRQ 或 CPU 的中断增长情况 脚本每秒读取 /proc/interrupts,计算变化量,只输出“发生变化的项”。
  • 支持 CPU 或 IRQ 白名单筛选 避免全量输出带来的噪声。
  • 支持稀疏 CPU(如 CPU0/7/8/9/12) 可自动解析实际在线 CPU,而不依赖连续编号。
  • 支持参数格式:1,5-7,10-12 满足生产环境中常见的范围表达方式。
  • 附带中断描述信息 如网卡 MSI-X、IPI、timer 等,便于快速识别来源
  • 兼容 Python 2 / Python 3 适用于老旧发行版(如 RHEL7)。

2. 参数说明

脚本提供三个最常用参数:

参数说明
-irq指定监控的 IRQ 号(支持范围表达式)
-cpu指定监控的 CPU 核(支持范围表达式)
-i刷新间隔,默认 1 秒

3. 示例

只监控 CPU0 与 CPU1 中断情况

./irq_watch.py -cpu 0,1

监控特定中断号(如 36、37、40–45)

./irq_watch.py -irq 36,37,40-45

监控多 CPU + 多 IRQ(组合过滤)

./irq_watch.py -cpu 0,7-9 -irq 33-50

4. 输出示例解析

以下是监控cpu0和cpu1上每秒中断的情况:

[root@instance-bguv65e0 ~]# ./irq_watch.py -cpu 0,1
2025-11-26 19:25:44
IRQ    37 CPU   0: +        10 (total  120514471)      PCI-MSIX-0000:02:00.0 1-edge virtio1-req.0
IRQ    38 CPU   1: +         5 (total  117021031)      PCI-MSIX-0000:02:00.0 2-edge virtio1-req.1
IRQ    45 CPU   0: +        13 (total   49011897)      PCI-MSIX-0000:01:00.0 1-edge virtio0-input.0
IRQ    46 CPU   0: +         5 (total   29506292)      PCI-MSIX-0000:01:00.0 2-edge virtio0-output.0
IRQ    47 CPU   1: +        13 (total   51737238)      PCI-MSIX-0000:01:00.0 3-edge virtio0-input.1
IRQ    48 CPU   1: +        21 (total   56302135)      PCI-MSIX-0000:01:00.0 4-edge virtio0-output.1
IRQ   LOC CPU   0: +       232 (total   41007210)      Local timer interrupts
IRQ   LOC CPU   1: +       186 (total  540671379)      Local timer interrupts
IRQ   CAL CPU   0: +         3 (total   26439851)      Function call interrupts
2025-11-26 19:25:45
IRQ    37 CPU   0: +         1 (total  120514472)      PCI-MSIX-0000:02:00.0 1-edge virtio1-req.0
IRQ    38 CPU   1: +         4 (total  117021035)      PCI-MSIX-0000:02:00.0 2-edge virtio1-req.1
IRQ    45 CPU   0: +         4 (total   49011901)      PCI-MSIX-0000:01:00.0 1-edge virtio0-input.0
IRQ    46 CPU   0: +         9 (total   29506301)      PCI-MSIX-0000:01:00.0 2-edge virtio0-output.0
IRQ    47 CPU   1: +         6 (total   51737244)      PCI-MSIX-0000:01:00.0 3-edge virtio0-input.1
IRQ    48 CPU   1: +         7 (total   56302142)      PCI-MSIX-0000:01:00.0 4-edge virtio0-output.1
IRQ   LOC CPU   0: +       234 (total   41007444)      Local timer interrupts
IRQ   LOC CPU   1: +       166 (total  540671545)      Local timer interrupts
IRQ   RES CPU   0: +         1 (total   16877997)      Rescheduling interrupts
IRQ   CAL CPU   0: +         3 (total   26439854)      Function call interrupts
2025-11-26 19:25:46
IRQ    45 CPU   0: +         4 (total   49011905)      PCI-MSIX-0000:01:00.0 1-edge virtio0-input.0
IRQ    46 CPU   0: +         8 (total   29506309)      PCI-MSIX-0000:01:00.0 2-edge virtio0-output.0
IRQ    47 CPU   1: +         5 (total   51737249)      PCI-MSIX-0000:01:00.0 3-edge virtio0-input.1
IRQ    48 CPU   1: +         4 (total   56302146)      PCI-MSIX-0000:01:00.0 4-edge virtio0-output.1
IRQ   LOC CPU   0: +       200 (total   41007644)      Local timer interrupts
IRQ   LOC CPU   1: +       204 (total  540671749)      Local timer interrupts
IRQ   RES CPU   0: +         7 (total   16878004)      Rescheduling interrupts
IRQ   RES CPU   1: +         2 (total   20872214)      Rescheduling interrupts
IRQ   CAL CPU   0: +         1 (total   26439855)      Function call interrupts

解释如下: IRQ:中断号(或 LOC/CAL 等 IPI 名字) CPU:发生增量的 CPU 核编号 +N:本次 interval 内增加多少次 total:累计总次数 描述:/proc/interrupts 最后一列,中断来源(网卡/磁盘/IPI/timer)

监控的中断数量没有变化会输出 (no change):

[root@instance-bguv65e0 ~]# ./irq_watch.py -irq 222
2025-11-26 19:44:35
no change
2025-11-26 19:44:36
no change
2025-11-26 19:44:37
no change

这样可大幅降低无意义噪声,便于观察异常增长。

五、典型排查场景

场景 1:业务核心是否被“脏”中断污染?

在延迟敏感的系统中,关键的业务线程往往会被绑定到固定的核心上,比如业务核心被绑定在 6,7,8,9号核心,则可以用以下指令来监测:

./irq_watch.py -cpu 6-9

场景 2:排查稀疏 CPU 布局下的中断落点

在禁用了部分核的系统中(如 CPU0/7/8/9/12 在线):

./irq_watch.py -cpu 7-12

脚本可自动识别真实在线 CPU,使排查更准确。

场景 3:观察周期性延迟尖刺是否由某 IRQ 触发

例如怀疑某个 timer 中断:

./irq_watch.py -irq LOC

或怀疑某特点中断

./irq_watch.py -irq 64-100

若被监控的中断数量周期性增长,则可以确定延迟尖刺原因。

六、irq_watch.py 的优势总结

能力描述
实时检测定位瞬时 spike 不再困难
增量输出不输出无变化项,噪声极低
CPU/IRQ 双过滤灵活监控业务相关部分
稀疏 CPU 兼容适配任意在线 CPU 布局
自动提取中断描述快速识别来源
资源占用低占用的硬件资源几乎可以忽略

七、如何获取?

📌 获取方式如下: 1️⃣ 关注我的微信公众号:Hankin-Liu的技术研究室 2️⃣ 发送关键词:irq watch 3️⃣ 获取脚本 4️⃣ 发送关键词:”性能调优群“,加入技术交流群,可免费解答和交流性能调优相关技术问题。

八、总结

低延迟系统需要在高度可控、可预测的环境中运行,而中断是影响系统确定性的关键因素之一。理解中断在系统中的角色、其带来的不确定性来源,以及如何持续监测其行为,是打造稳定低延迟系统的基础能力。 通过构建完善的监控机制、结合业务特性制定相应策略,并持续验证系统在实际负载下的表现,可以更好地确保低延迟系统在各种运行状态下保持一致的性能表现。中断管理不是一次性的工作,而是伴随系统运行全周期的持续任务。

📬 欢迎关注VX公众号“Hankin-Liu的技术研究室”,持续分享信创、软件性能测试、调优、编程技巧、软件调试技巧相关内容,输出有价值、有沉淀的技术干货。