进程和操作系统的诊断工具
系统变慢时,先区分 CPU 忙、任务在等待、内存紧张、磁盘空间不足,还是网络连接异常。不同工具观察的是不同层次:top 展示资源概况,ps 定位进程与线程,ss 观察套接字,strace 跟踪系统调用。单个指标只能缩小范围,需要同一时间窗口内的其他证据支撑判断。
本文面向 Linux,主要采用 procps-ng、GNU coreutils、iproute2 的命令格式。macOS 的选项和字段不同,也没有 Linux 的 /proc 接口;精简容器可能缺少部分命令。示例中的 PID 12466、端口 8080 和路径 /var/log 均需替换为实际目标。输出字段随工具版本变化,应以本机 man 为准。
从现象选择排查入口
先记录时间、目标 PID、进程启动时间和所在容器,再连续采样。快照适合确认“现在是什么”,区间统计适合回答“这几秒发生了什么”。进程重启后 PID 可能复用,不能把重启前后的数据直接拼起来。
图 1:从系统现象逐步定位到进程证据。
flowchart TD
accTitle: 从系统现象到进程证据的排查路径
A["延迟升高或服务异常"] --> B["uptime / top / free / df<br/>确认资源概况"]
B --> C{"主要异常在哪"}
C --> D["CPU 或 load<br/>top -H / vmstat"]
C --> E["内存<br/>free / status / smaps"]
C --> F["空间或 I/O<br/>df / du / iostat / iotop"]
C --> G["连接或端口<br/>ss / lsof"]
D --> H["定位 PID、TID 与采样时间"]
E --> H
F --> H
G --> H
H --> I["必要时短时 strace<br/>与应用日志交叉验证"]
可以从这组低成本快照开始:
1 | |
vmstat 的第一份报告中,CPU 和速率统计通常是启动以来的平均值;后续报告才对应采样间隔,进程数和内存字段则是瞬时值。支持 -y 的版本可用 vmstat -y 1 5 跳过首份报告。不要把第一行与后续区间当成同一种数据。vmstat 手册
进程与线程:ps、pstree
确认进程身份和父子关系
1 | |
ps aux 使用 BSD 风格选项;不要混写成容易产生歧义的 ps -aux。PPID 表示父进程 ID,--forest 的缩进显示父子关系。nohup 主要改变挂断信号的处理方式,缩进不是“使用 nohup 启动”的标志。ps 手册
例如下面的示意树中,1、35、42 是进程,花括号中的 43、44 是进程 42 的线程。实际显示方式受 pstree 选项和实现影响。
1 | |
图 2:父子进程关系与进程内线程的区别。
flowchart TD
accTitle: 父子进程与进程内线程
A["PID 1<br/>容器入口"] --> B["PID 35 / PPID 1<br/>启动脚本"]
B --> C["PID 42 / PPID 35<br/>应用进程"]
C -. "同属一个线程组" .-> D["TID 42<br/>主线程"]
C -. "同属一个线程组" .-> E["TID 43<br/>工作线程"]
C -. "同属一个线程组" .-> F["TID 44<br/>工作线程"]
下钻到线程和等待状态
1 | |
| 状态 | 含义 | 后续观察 |
|---|---|---|
| R | 正在运行或等待 CPU | 结合 CPU 利用率、运行队列和线程采样 |
| S | 可中断睡眠 | 常见于等待事件,单独出现不代表异常 |
| D | 不可中断睡眠 | 结合等待点与 I/O,不能直接断言磁盘损坏 |
| T / t | 停止或被跟踪 | 检查信号与调试状态 |
| Z | 已退出但尚未被父进程回收 | 检查父进程的子进程回收逻辑 |
进程级 CPU 异常可以继续缩小到 TID,但操作系统线程 ID 不等于语言运行时中的线程名称。关联 JVM 线程栈时,还需要核对线程标识及其进制表示。
CPU 与负载:top、uptime、vmstat
load average 不是 CPU 利用率
uptime 与 top 中的三个 load average 分别对应 1、5、15 分钟的负载平均值。在 Linux 中,负载统计可运行任务和不可中断睡眠任务,包含线程;它不是每分钟处理的进程数,也不能换算成 CPU 利用率。uptime 手册
4 个逻辑 CPU 的机器,如果负载主要来自可运行任务,持续接近 4 可以提示 CPU 并发需求接近可用容量;如果其中大量任务处于 D 状态,同样的 load 可能伴随较低 CPU 利用率。容器还需考虑 CPU 配额与亲和性,宿主机核数不一定等于容器可用算力。
图 3:同样的高 load 可以对应不同的 CPU 使用情况。
flowchart TD
accTitle: Linux 负载构成与 CPU 时间的区别
A["Linux load average"] --> B["R:运行中或等待 CPU"]
A --> C["D:不可中断睡眠"]
B --> D["与可用 CPU 容量比较<br/>观察 vmstat r 和 top"]
C --> E["检查等待点与 I/O<br/>观察 vmstat b 和设备统计"]
D --> F["CPU 忙且 r 持续较高<br/>支持 CPU 竞争判断"]
E --> G["CPU 仍可能空闲<br/>load 高不等于 CPU 忙"]
1 分钟值高于 5、15 分钟值,通常表示近期负载正在上升;反过来可能正在恢复。三个窗口都值得观察,不能只看 15 分钟,也不能因为短时升高就判断“问题不大”。
top 的 CPU 时间如何读
| 字段 | 含义 | 判断边界 |
|---|---|---|
| us / ni | 普通用户态 / 调整过 nice 的用户态时间 | 需要线程或栈采样定位代码 |
| sy | 内核态执行时间 | 高值可能涉及系统调用等,不能单凭它认定竞态 |
| id | 空闲时间 | 系统汇总值可能掩盖单核繁忙 |
| wa | I/O 等待相关的 CPU 时间计账 | 不是磁盘利用率,多核下计账有局限 |
| hi / si | 硬中断 / 软中断处理时间 | 需进一步查看中断来源 |
| st | 虚拟机等待期间被 hypervisor 取走的 CPU 时间 | 提示虚拟化调度影响,不能直接确定其他虚拟机的行为 |
top 的进程 %CPU 默认常按单个逻辑 CPU 为 100% 计量,多线程进程可超过 100%;切换归一化显示模式后口径会变化。ps %CPU 通常是进程整个生命周期内的平均值,与 top 刷新间隔内的值不能直接比较。top 手册
wa 的多核归属与可靠性存在限制。低 wa 不能排除单个服务正在等待 I/O,高 wa 也不能独立定位设备瓶颈。内核 proc 文档
vmstat 把队列、换页和 CPU 放在同一时间窗口
1 | |
| 字段 | 含义 | 可关联的证据 |
|---|---|---|
| r / b | 可运行任务数 / 阻塞等待 I/O 完成的任务数 | CPU 容量、D 状态及等待点 |
| swpd | 已用 swap 大小 | free 中的 swap 与进程 VmSwap |
| si / so | 从 swap 换入 / 换出的速率 | 持续换页、内存压力与延迟 |
| bi / bo | 块设备读入 / 写出速率 | iostat 的设备统计 |
| in / cs | 每秒中断 / 上下文切换次数 | 与相同负载下的基线比较 |
| us / sy / id / wa / st | CPU 时间分类 | top 与线程级观察 |
当前 procps-ng 手册中的 bi/bo 以 KiB/s 计,内存字段和 si/so 的单位还要留意版本及 -S 选项。上下文切换高说明切换频繁,是否有问题取决于工作负载与基线。
内存:free、top、pmap、smaps
系统剩余多少内存,先看 available
1 | |
free 低不一定意味着内存不足。文件缓存和部分内核缓存可以回收;available 是无需交换即可供新应用使用的内存估计,已考虑可回收部分和必要保留量。它不是“缓冲的交换区大小”。free 手册
| 字段 | 含义 |
|---|---|
| total / free | 可用物理内存总量 / 完全未使用的内存 |
| available | 无需交换即可供新应用使用的内存估计 |
| buff/cache | 缓冲、页缓存及相关可回收内核缓存 |
| shared | 主要对应 tmpfs 等使用的 Shmem,并非所有共享映射的总量 |
| Swap used | 已占用的交换空间,不能据此认定当前正在频繁换页 |
当前 procps-ng 的 used 按 total - available 计算。旧版本曾有不同计算方式,并出现 -/+ buffers/cache 行;解释历史截图时要先确认版本,不能套用同一个公式。
如果 available 持续下降,同时 vmstat si/so 持续活跃并伴随延迟增加,才形成更完整的内存压力证据。容器还需核对自身内存限制,主机有空闲内存不保证容器不会触及上限。
VIRT、RSS、PSS 分别回答什么
| 指标 | 观察对象 | 常见误读 |
|---|---|---|
| VIRT / VmSize | 进程虚拟地址空间大小,包含映射和预留等 | 不能等同于实际 RAM,也不能套用 VIRT = RES + SWAP |
| RES / RSS / VmRSS | 常驻物理内存,包含常驻的文件映射和共享页 | RSS 不是固定以页数表示,各接口单位不同 |
| PSS | 将共享页按共享者数量分摊后的常驻量 | 适合减少跨进程汇总时的重复计算 |
| SHR | top 中可能被其他进程共享的常驻部分 | 不能直接用 RES − SHR 当作精确私有内存 |
| DATA | 私有数据等虚拟内存范围,具体分类看版本 | 不一定已驻留,不能用 RES = CODE + DATA |
| VmSwap | 私有匿名页的换出量,不含 shmem 的 swap | 不是“已申请但未使用的空间” |
图 4:地址空间、常驻页和共享页分摊的关系。
flowchart TD
accTitle: 虚拟地址空间、RSS 与 PSS 的计量关系
A["进程虚拟地址空间 VIRT"] --> B["当前驻留的页面<br/>计入 RES / RSS"]
A --> C["已换出的匿名页<br/>不计入 RSS"]
A --> D["未驻留的其他映射<br/>如尚未触页或文件页不在内存"]
B --> E["私有驻留页<br/>RSS 与 PSS 均全额计入"]
B --> F["共享驻留页<br/>每个进程 RSS 均全额计入"]
F --> G["PSS 按共享者数量分摊<br/>4 KiB 页由两进程共享时各计 2 KiB"]
例如,两个进程各有 8 MiB 私有驻留页,并共同映射同一组 4 MiB 驻留共享页,则每个进程 RSS 为 12 MiB、PSS 为 10 MiB。仅在这个简化模型中,两者实际合计使用 20 MiB 物理页,而 RSS 相加会得到 24 MiB。内核 proc 文档
从进程总量下钻到映射
1 | |
pmap -x 适合查看映射分布;smaps 提供每个映射的 Size、Rss、Pss、共享/私有及干净/脏页信息。smaps_rollup 提供汇总,避免自行累加数百个映射,但读取仍有页表扫描成本,不宜高频轮询。
status 中的 VmHWM 是常驻量峰值,VmPeak 是虚拟内存峰值。VmRSS 等快速统计可能不够精确,需要更细致核算时再用 smaps。仅凭 VmStk 不能得到 JVM 所有线程的本地栈总量。proc_pid_status 手册
排查单个进程 swap 可直接读取它的 VmSwap。跨进程遍历 /proc/*/status 时,进程可能退出,权限也可能不同,结果只是一次近似快照。
磁盘空间与 I/O:df、du、iostat、iotop
df 看文件系统,du 看目录树
1 | |
df 回答文件系统还有多少可用空间;df -i 检查 inode 是否耗尽。du 遍历目录,默认统计文件占用的磁盘块,du --apparent-size 则按表观大小统计。稀疏文件、硬链接、挂载点和权限都可能影响比较。
sort -h 才能按 K、M、G 等可读单位排序。直接 sort 是字符串排序,grep G 还会漏掉其他单位。上面的递归扫描适合缩小范围后使用,大目录扫描本身也会增加 I/O。
图 5:文件删除后仍被进程打开时,df 与 du 为何可能不一致。
flowchart TD
accTitle: 已删除但仍打开的文件造成 df 与 du 差异
A["进程打开日志文件"] --> B["删除最后一个目录链接"]
B --> C["du 遍历目录<br/>已无法找到这个文件"]
B --> D["进程仍持有文件描述符"]
D --> E["文件数据块仍被占用<br/>df 继续计入"]
E --> F["lsof +L1 查找线索"]
F --> G["持有者关闭且相关引用释放后<br/>空间才可回收"]
1 | |
已删除但仍打开的文件只是 df/du 差异的一种来源,还应确认比较的是同一文件系统、相同挂载视图及可访问目录。排查阶段先确认持有者,再决定如何让应用关闭或重新打开文件。GNU 磁盘用量工具说明
空间不足和 I/O 慢是两种问题
df 正常只能说明容量尚有余量,不能证明存储延迟正常。设备级观察可用 sysstat 提供的 iostat,进程级归因可用 iotop。
1 | |
iostat 的 await 包含请求排队与服务时间;%util 是设备存在 I/O 活动的时间比例,对并行设备不能直接当作“性能上限百分比”。结合请求量、延迟和队列变化判断,首份报告也要注意启动以来统计的口径。iostat 手册
iotop 的 -o 只显示有活动的任务,-P 按进程展示,-a 观察累计量。I/O 等待百分比等字段依赖内核配置、权限与实现版本;缓存和异步写回也会使进程读写与设备实际读写不同步。iotop 手册
网络:ss、netstat、lsof
分清端口、连接和进程
1 | |
ss 来自 iproute2,netstat 来自 net-tools;它们不是同一工具的不同名字。-p 能显示多少进程信息取决于权限。容器内看到的套接字还受网络命名空间限制,空结果不等于宿主机没有监听。ss 手册
图 6:从监听端口找到持有套接字的进程。
flowchart TD
accTitle: 端口、套接字、文件描述符和进程的定位关系
A["TCP 监听端口 8080"] --> B["ss -lntp<br/>查监听套接字及持有者"]
B --> C["套接字"]
C --> D["PID 12466 的 FD 57"]
D --> E["ps / proc status<br/>确认进程身份"]
E --> F["lsof -a -p 12466 -i<br/>查看该进程网络文件"]
监听只证明本地套接字处于 LISTEN,不能证明防火墙、路由和应用请求处理都正常。服务可达性仍需连接测试与应用日志支撑。
lsof:PID 不是端口号
1 | |
lsof -i:12466 查询的是端口 12466,不能因为 jps 输出 PID 12466 就这样使用。-nP 关闭主机名与服务名解析,-a 让 PID 与网络选择条件取交集;省略 -a 可能扩大为多个选择条件的并集。lsof 手册
常用列包括 COMMAND、PID、USER、FD、TYPE 和 NAME。FD 除数字描述符外,也可能是 cwd、txt、mem 等特殊项,因此不能把 lsof 的所有输出行都算作打开的数字 FD。
系统调用:strace
当进程级指标只能说明“在等”或“频繁进入内核”,可用系统调用记录进一步检查文件访问、连接失败和等待操作。
1 | |
-f 用于跟踪子进程,在 Linux 附加多线程进程时也可覆盖其线程;-tt 增加时间戳,-T 显示每次系统调用的经过时间,通常以秒的小数表示。-c 默认汇总的是系统调用 CPU 时间,不能直接当作请求端到端延迟。strace 手册
futex 等待可能是正常线程同步,epoll_wait 可能只是服务空闲,单次 ENOENT 也可能是正常路径探测。要结合持续时间、频率、调用参数和应用日志判断。
附加受同用户权限、ptrace 策略和容器能力约束;跟踪还会改变被观测进程的时序并增加开销。输出可能包含路径、网络数据或业务参数,应控制采样时长和日志访问范围。仅看到系统调用无法定位纯用户态计算热点,CPU 栈采样需使用其他工具。
/proc:从汇总指标回到内核接口
Linux 的 /proc 是内核通过 procfs 暴露的运行态接口,许多文件在读取时生成内容,显示大小为 0 仍可读取。它不是普通磁盘目录;其中部分接口可写,诊断读取与修改内核参数是两种不同操作。
图 7:系统级、进程级和线程级的 /proc 入口。
flowchart LR
accTitle: procfs 的系统、进程与线程观察入口
A["/proc"] --> B["系统级"]
A --> C["/proc/PID"]
B --> D["loadavg / stat<br/>负载与 CPU"]
B --> E["meminfo / vmstat<br/>内存与虚拟内存统计"]
B --> F["diskstats / net/dev<br/>设备与网络统计"]
C --> G["status / limits / io<br/>状态、限制和 I/O"]
C --> H["maps / smaps / smaps_rollup<br/>地址映射与内存"]
C --> I["fd / cwd / exe / root<br/>文件及路径视图"]
C --> J["task/TID<br/>线程级入口"]
系统级常用入口
| 路径 | 内容 | 读取边界 |
|---|---|---|
| /proc/cmdline、/proc/version | 内核启动参数、内核版本 | 不等于应用启动参数或完整发行版信息 |
| /proc/cpuinfo、/proc/stat、/proc/loadavg | CPU 信息、累计 CPU 计数、负载 | cpuinfo 条目数不能直接当作容器可用算力 |
| /proc/meminfo、/proc/vmstat | 内存概况、虚拟内存计数 | 先区分存量和累计计数,再计算区间变化 |
| /proc/diskstats、/proc/net/dev | 块设备、网卡统计 | 计数通常需两次采样做差 |
| /proc/swaps、/proc/partitions、/proc/mounts | 交换设备、分区和挂载 | 注意命名空间与挂载视图 |
| /proc/filesystems、/proc/devices、/proc/modules | 文件系统支持、设备号与已加载模块 | 可用内容受内核配置影响 |
| /proc/interrupts、/proc/softirqs | 中断和软中断计数 | 配合 CPU 指标定位来源 |
| /proc/kcore | 内核内存的 ELF core 视图 | 显示很大不表示占用对应磁盘空间 |
控制台入口是 /proc/consoles,交换设备列表是 /proc/swaps。/proc/kcore 的显示大小不宜套固定公式;dmesg 的读取方式也不宜简化成“就是读取 /proc/kmsg”,现代实现可使用其他内核日志接口。proc 手册
进程目录常用入口
| 路径 | 用途 | 容易混淆的地方 |
|---|---|---|
| cmdline | 进程参数,通常以 NUL 分隔 | 不是内核 cmdline |
| environ | exec 时的初始环境,以 NUL 分隔 | 空格没有被删掉,后续环境修改通常不在这里反映 |
| cwd / exe | 工作目录 / 可执行文件的符号链接 | 工作目录不必等于程序安装目录 |
| root | 进程看到的文件系统根 | 体现其挂载视图,与 JAVA_HOME 无关 |
| fd / limits | 打开的描述符 / 资源限制 | 读取其他用户的信息可能受权限限制 |
| maps / smaps / smaps_rollup | 映射范围 / 逐映射内存 / 汇总 | 字段和可用性随内核版本变化 |
| status / stat / statm | 可读状态 / 紧凑状态 / 内存统计 | statm 的 resident 以页计,VmRSS 以 kB 标注 |
| io / task | I/O 计数 / 线程子目录 | 计数需做区间差,线程应核对 TID |
1 | |
environ 使用相同的 NUL 分隔方式,按行查看可用 tr '\0' '\n' < /proc/12466/environ,但环境变量可能包含凭据,不应直接复制到公开日志。environ 手册、root 手册
按现象回查工具
| 现象 | 首选观察 | 下钻方向 | 判断边界 |
|---|---|---|---|
| CPU 忙或 load 高 | top、uptime、vmstat | ps -L、线程采样、等待点 | load 包含不可中断睡眠,CPU 口径受核数和配额影响 |
| 内存紧张 | free、vmstat | status、pmap、smaps | available 看余量,si/so 看换页,PSS 减少共享页重复计数 |
| 磁盘满或 I/O 慢 | df、df -i、iostat | du、lsof +L1、iotop | 容量、inode、设备延迟需分别观察 |
| 端口或连接异常 | ss、lsof | 进程身份、连接测试、应用日志 | LISTEN 不代表业务请求成功 |
| 启动失败或行为不明 | 应用日志、ps、/proc | 短时 strace | 系统调用耗时不等于完整请求耗时 |
参考资料
字段解释以各节链接的一手手册为依据;阅读历史系统输出时,优先对照目标机器安装版本的 man。

