惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 聂微东
Jina AI
Jina AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
Tailwind CSS Blog
罗磊的独立博客
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - Franky
阮一峰的网络日志
阮一峰的网络日志
雷峰网
雷峰网
博客园 - 叶小钗
美团技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
The Cloudflare Blog
Last Week in AI
Last Week in AI
S
SegmentFault 最新的问题
博客园 - 【当耐特】
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
量子位
宝玉的分享
宝玉的分享
V
Visual Studio Blog
博客园_首页
IT之家
IT之家
V
V2EX
腾讯CDC
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Microsoft Security Blog
Microsoft Security Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Blog — PlanetScale
Blog — PlanetScale
I
InfoQ
有赞技术团队
有赞技术团队
J
Java Code Geeks
Recorded Future
Recorded Future
Engineering at Meta
Engineering at Meta
Vercel News
Vercel News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
H
Help Net Security

暗无天日

读:AI Agent 安全日志——从可见性与隐私的两难说起 - 暗无天日 AI写作的语言指纹——如何让文字不那么像机器 - 暗无天日 读:50 条 Claude Code 技巧——一个工程经理的六个月使用心得 读:AI 辅助开发为什么让 E2E 测试更有价值 - 暗无天日 读:在Emacs中使用Claude Code(Spacemacs适配版) - 暗无天日 Claude Code 背后的工程哲学——读 Agent Harness Engineering 读:Agent Harness Engineering——AI 智能体不只是模型,还有套件 - 暗无天日 browser-harness:让 AI 直接接管你的浏览器 - 暗无天日 读:Security-First CI/CD —— DevSecOps 自动化实践指南 TIL: 数字小键盘的小数点陷阱与行内算术求值 - 暗无天日 读:Immutability 不是万能药,它是一种权衡 - 暗无天日 Conducty:给 Claude Code 加上项目记忆和并行执行能力 - 暗无天日 读 — GitHub Trending 里的 Claude Code 技能包 读 — Prompt Caching 省钱指南 TIL: Emacs 中那些跟鼠标配合的冷门快捷键 - 暗无天日 读:Anvil——把 Emacs 变成 AI 的工具服务器 读:Emacs 代码折叠终极指南 - 暗无天日 读:Clojure 搭车客指南 - 暗无天日 git推送失败后恢复仓库损坏的完整记录 - 暗无天日 多智能体系统的两个有效模式——以及对 Claude Code 用户的启示 - 暗无天日 用 Org Babel 写 Literate 博文:扩展执行 + 定制导出 proced:Emacs 内置的进程查看器 - 暗无天日 从 proced 定制中学到的 Elisp 模式 读:让 Emacs proced 在 macOS 上显示 CPU 和内存 异步编程的函数着色税 - 暗无天日 链式调用的代价:JavaScript 和 Clojure 的共同教训 - 暗无天日 hyperfine:命令行基准测试工具 - 暗无天日 管道中的变量去哪了?——子 shell 作用域陷阱 - 暗无天日 开源包装器的信任陷阱:四个危险信号 - 暗无天日 程序员愿意为 AI 写文档,却不愿为同事写 - 暗无天日 mktemp: Shell 脚本中临时文件的安全陷阱与最佳实践 - 暗无天日 WSL9x —— 在 Windows 9x 里跑 Linux 内核 6.19 用 ox.el 做你想做的事 —— org-export 高级编程指南 读:Hot-wiring the Lisp Machine —— 用纯 Elisp 构建零依赖的 Org 静态站点生成器 Elisp 性能优化的六个实战教训 - 暗无天日 fcitx5 下 Emacs 无法切换输入法的排查 - 暗无天日 ERT 测试交互命令的三种方式 - 暗无天日 SEM Assistant: 当 Elisp 守护进程遇上 LLM 用 dmsg 给 Elisp 加上结构化调试日志 用 org-habit 追踪非每日习惯 - 暗无天日 Clojure X-Men:当编程语言特性变成超能力 - 暗无天日 TIL: 用 diff-hl 在 fringe 中显示 git 变更 读:llm-test —— 用 LLM agent 驱动 Emacs 测试 TIL: AI 时代的橡皮鸭调试 - 暗无天日 fcitx 启动后键盘输入卡顿的排查 - 暗无天日 TIL: 早期网页的图片热区导航 - 暗无天日 读 Seeing the Whole System 用 Emacs 自动生成每周链接推荐 - 暗无天日 读:ASCII control characters in my terminal 读 What to learn - 暗无天日 Lisp 的括号之痛——一个愚人节玩笑揭开的老伤疤 - 暗无天日 一本书该"线性读"还是"并行读" - 暗无天日 读 How to Monetize a Blog:一篇伪装成变现指南的讽刺文 Python Mock 第三方依赖的四种策略 - 暗无天日 Emacs Lisp 热重载实用指南 - 暗无天日 Prot 的 Emacs 配置哲学 - 暗无天日 TIL: 从直播对谈中学到的三个 Emacs 技巧 - 暗无天日 TIL: 自动使用项目虚拟环境的 Python - 暗无天日 TIL: 让 Help buffer 自动获得焦点 一条命令让本地开发用上 HTTPS —— slim 工具介绍 用 fsck 检查和修复 Linux 文件系统 排查Linux进程"卡死"实战:从strace到gdb全流程 - 暗无天日 PostgreSQL 索引:从基础到你可能不知道的高级用法 - 暗无天日 用 .pdbrc 自定义 Python 调试器 ANSI 转义码的标准化现状 - 暗无天日 终端程序的潜规则 - 暗无天日 PARA Org-mode 测试配置 - 暗无天日 AI越强越辣鸡?控制论说这是必然的 - 暗无天日 AI 越强越需要你盯着——反馈循环实操指南 - 暗无天日 你的AI代理正在偷你的密钥——四种你没想到的泄露通道 - 暗无天日 LLM 在 DevOps 中的三种角色 - 暗无天日 写作风格的反建议 - 暗无天日 反驳本质复杂性——Dan Luu 论为什么《没有银弹》错了 - 暗无天日 文件充满了危险——Dan Luu 谈文件系统的可靠性陷阱 - 暗无天日 AI 时代的 PARA 方法:用 Org-mode 和 AI 打造个人知识管理系统 Linux 数据去重学习笔记 - 暗无天日 创建跨平台 ZIP 文件的隐藏陷阱:Extra Field - 暗无天日 X11 Forwarding 排障指南 - 暗无天日 IP欺骗端口扫描:当别人冒充你去扫描别人 - 暗无天日 Linux 输入栈全景解析:从硬件按键到屏幕响应 - 暗无天日 Unix 系统中那些被埋没的配置开关——以 FontConfig 为例 - 暗无天日 在Linux上限制儿童使用电脑 - 暗无天日 GIF不仅仅是一种图片格式——用GIF流做些奇怪的事 - 暗无天日 Leiningen 学习笔记:Clojure 项目构建与管理从入门到实战配置 - 暗无天日 Google SRE Book 读书笔记 - 暗无天日 yes 管道 head 发生了什么 - 暗无天日 为什么 nohup 在 crontab 中不起作用 Bash中的Indirection与Nameref - 暗无天日 Linux PAM 简介 - 暗无天日 从Linux ISO文件启动计算机 - 暗无天日 用 Bash 打造一个Screen Locker 用GitHub Actions自动构建EGO博客 - 暗无天日 blocking I/O 的作用 - 暗无天日 mobileog 手机端同步提示Error:2 No such file 的解决方法 回收 WSL2 VHDX 文件占用空间 使用 org-mode columnview 生成任务列表 - 暗无天日 Emacs 作为 MPD 客户端 - 暗无天日 移动文件路径却不破坏org file link的方法 - 暗无天日 如何合理的导出help link 成HTML - 暗无天日 笑话理解之Biology - 暗无天日
读:sysstat 诊断链——从 sar 到 pidstat 的排查路径
2026-05-06 · via 暗无天日

引子

系统变慢的时候,大多数人的第一反应是敲 top 。这没问题。 top 给你一个即时快照:谁在用 CPU、内存还剩多少、load average 大概什么水平。

top 有两个盲区。第一,它只看当下。凌晨 3 点发生的性能问题,你早上 9 点坐到工位时 top 已经什么都看不到了。第二, top 能看到每个进程用了多少 CPU,但很难同时看清三件事:哪个核在忙、忙的是哪个线程、当时有多少进程在排队等 CPU。这三个信息分散在不同的视图里,手工拼凑太慢。

sysstat 套件补上了这两个盲区。它包含四个核心工具: mpstatpidstatsariostat 。TecMint 上有篇 文章 系统介绍了它们的用法。本文不逐条翻译那 20 个示例,改用一条真实的排查链路把它们串起来,从发现异常到定位根因。

安装和启用

sysstat 大部分发行版默认不装:

sudo apt install sysstat
sudo dnf install sysstat
sudo pacman -S sysstat

装完确认版本:

mpstat -V
sysstat 版本 12.7.9
(C) Sebastien Godard (sysstat <at> orange.fr)

Ubuntu 用户特别注意 :安装后数据收集默认是关闭的。需要编辑 /etc/default/sysstat ,把 ENABLED="false" 改成 ENABLED="true" 。否则 sar 不会自动记录历史数据,而这是整篇文章最有价值的功能,漏掉等于白装。

诊断第一环:sar -u 回看历史

场景:早上到工位,同事说昨晚系统很卡。你可以通过 sar 回顾当时的系统指标。

sar 通过 cron 定时采集系统指标,存成二进制日志。你可以指定任意时间段回放。

配好 cron 自动收集(这一步是整篇文章最重要的事):

*/10 * * * * /usr/lib/sysstat/sa1 1 1   53 23 * * * /usr/lib/sysstat/sa2 -A

sa1 / sa2 的路径因发行版不同:

  • Debian/Ubuntu: /usr/lib/sysstat/sa1
  • RHEL/Rocky: /usr/lib64/sa/sa1
  • Arch Linux: /usr/lib/sa/sa1

Debian/Ubuntu 下 which sa1 能找到,Arch 下 sa1/usr/lib/sa/ 而不在 PATH 里,用 ls /usr/lib/sa/sa1 确认。确认路径后替换 cron 条目中的对应位置。

确认数据在积累:

ls -lh /var/log/sysstat/

回放历史 CPU 数据:

sar -u -f /var/log/sysstat/sa$(date +%d)

sar -u 的输出项跟 mpstat 类似( %=user%=system%=iowait 等),关键在于它能告诉你问题发生的具体时段。知道时间点之后,后面的工具才有用武之地。

如果你不需要回放历史、只想看当前的连续采样,让 sar 交互式跑几轮:

sar -u 2 5

2 5 的意思是每 2 秒采一次,采 5 次。最后会有一行 Average: 汇总。

诊断第二环:mpstat -P ALL 定位热核

sar 告诉你"昨晚 10 点到 11 点 CPU 偏高",接下来要回答"是所有核都忙,还是单核被打满"。

mpstat 报告逐核 CPU 使用率。不加参数时只给一个全局汇总:

mpstat

加了 -P ALL 之后,每个 CPU 核心一行:

mpstat -P ALL 2 5

这时候你可能看到类似这样的输出:

CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
all   19.87    0.01    3.02    0.88    0.00    0.08    0.00    0.00    0.00   76.14
  0   72.14    0.00    5.22    0.00    0.00    0.00    0.00    0.00    0.00   22.64
  1   14.32    0.03    2.88    0.52    0.00    0.08    0.00    0.00    0.00   82.17
  2   10.44    0.02    2.71    1.98    0.00    0.12    0.00    0.00    0.00   84.73
  3    8.11    0.01    1.75    3.52    0.00    0.09    0.00    0.00    0.00   86.52

CPU 0 的用户态使用率 72%,其他三个核几乎空闲。这是经典的单线程进程打满单核的特征。

几个值得注意的列:

  • %iowait :持续超过 10-15% 意味着系统在等磁盘读写。这时候加 CPU 没用,问题在磁盘。
  • %soft :软件中断占比。网络数据包到达后,内核需要软中断来处理协议栈(TCP/IP 拆包、路由等)。 %=soft 高通常意味着网络流量打满了,CPU 在忙于收发包而非跑你的应用程序。

如果怀疑中断不均衡(比如网卡中断全压在 CPU 0 上),用 -I ALL 看中断分布:

mpstat -I ALL

NET_RX/s 集中在某个核上、同时该核 %soft 偏高,说明网卡中断亲和性(网卡硬中断固定发给某个 CPU 处理)需要调整。要么用 irqbalance 服务自动分配中断到多个核,要么手动写 /proc/irq/<编号>/smp_affinity 把特定中断号绑到其他 CPU 上。

诊断第三环:pidstat 找到凶手进程

mpstat 告诉你"CPU 0 被打满了",下一个问题自然是"谁打的"。

pidstat 报告进程级别的 CPU、内存、I/O 使用率。不加参数列出所有活跃进程:

pidstat
UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
1000     2841   18.44    0.88    0.00    0.00   19.32     0  python3
1000     3102    0.12    0.04    0.00    0.00    0.16     2  nginx

CPU 列显示该进程最后跑在哪个核上,和 mpstat 的热核直接对应。

%=wait 是一个容易被忽略但非常重要的列(sysstat 11.5 引入):它表示进程就绪后在运行队列里等待 CPU 调度的时间。这项持续非零,意味着 CPU 不够用,就绪队列里堆了太多工作,不是进程本身慢。

有了可疑的 PID,用 -t 展开线程看细节:

pidstat -t -p 2841 2 3
TGID       TID    %usr %system  %guest   %wait    %CPU   CPU  Command
2841         -   18.00    0.88    0.00    0.00   18.88     0  python3
   -      2841   17.50    0.50    0.00    0.00   18.00     0  |__python3
   -      2844    0.50    0.38    0.00    0.00    0.88     1  |__ThreadPool-1

输出里主线程( python3 )占 18.00% CPU,线程池( ThreadPool-1 )仅 0.88%。而且两行都是 %wait 为零、 %=usr 占大头,说明线程没有在等 CPU,也没有在等 I/O,就是在做纯用户态计算。这样可以定位:CPU 消耗集中在这个进程的主线程上,检查主线程的执行路径就能找到耗 CPU 的代码。

如果进程名已知,用 -G 直接过滤,不用在进程列表里找:

pidstat -G nginx 2

诊断第四环(条件):追 I/O 和内存

如果 mpstat 发现 %iowait 偏高

pidstat -d 报告每个进程的磁盘读写量:

pidstat -d 2
PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
2841    412.00     88.00      4.00      12  python3

iodelay 列是非零值时,该进程就是在等 I/O。和 mpstat 里高 %iowait 互相印证,凶手就确认了。

sar -d 则从块设备层面看延迟:

sar -d 1 3
  • await :请求从排队到完成的总时间
  • aqu-sz :平均队列深度

await 持续高但 aqu-sz 接近零,说明磁盘本身就慢(比如机械盘随机读写)。 await 高且 aqu-sz 也在 1 以上,则是排队已形成,磁盘处理不过来。

注意:旧文档常提的 svctm 字段在新内核中已废弃,sar 输出里该列不再可靠,不要依赖它做判断。

如果怀疑内存压力

sar -r 报告内存使用情况。重点看 %=commit 列:

sar -r 1 3

%=commit 是内核承诺给进程的虚拟内存总量相对于物理内存 + swap 的比例。超过 100%,意味着所有进程同时要求兑现承诺的话,OOM killer 就会出动。

pidstat -r 则从进程视角看内存:

pidstat -r -p 2841 2 3

VSZ 是虚拟内存, RSS 是物理内存。重点看 majflt/s (主缺页)列:主缺页意味着内核必须从磁盘捞回被换出的页面,持续非零说明系统在颠簸(thrashing),进程响应速度会明显下降。次缺页( minflt/s )不贵,只是内核在物理内存里映射页面而已。

贯穿始终:sar 历史 + sadf 导出

sar 真正的价值不是交互式采样(这个 mpstat 和 pidstat 也能做),而是自动收集的历史数据。配好 cron 后,任何发生过的性能问题你都可以事后回放。

回放历史数据时,用 sadf -d 导出为 CSV 格式,可以在表格里画图、和同事分享:

sadf -d /var/log/sysstat/sa$(date +%d) -- -n DEV | grep -v lo

排查链总结

整个过程是一条线:

  1. sar -u 回看历史,确定问题时段
  2. mpstat -P ALL 定位热核,看是均匀分布还是单核瓶颈
  3. pidstat 找到具体进程, -t 展开线程定位
  4. 如果 %iowait 高, pidstat -d + sar -d 追磁盘;如果怀疑内存, sar -r + pidstat -r 查换页

最值得现在做的事:把 sar 的 cron 采集配好,攒一周基线数据。有问题发生时,有历史数据和没有历史数据,排查时间是分钟级和小时级的差别。