系统资源监控:htop/iotop/nmon/du

2824 字
14 分钟
系统资源监控:htop/iotop/nmon/du

生信服务器的三类典型故障:(1) 未限制线程数导致 CPU 打满、SSH 无法连接;(2) 临时文件写爆 root 分区导致 BAM 损坏;(3) 内存泄漏触发 OOM Killer 杀进程。本文覆盖四类监控:进程(htop)、磁盘(iotop/du/ncdu)、内存(free/vmstat)、长期趋势(nmon/sar),附 6 个实战案例和 7 个踩坑记录。

环境:Debian 12,128 核 / 512GB 内存 / 10TB 存储(典型生信服务器配置)。

1. CPU与进程监控——htop 一屏看尽#

1.1 安装与基础#

Terminal window
sudo apt install htop -y
htop

进入 htop 后的界面布局(从上到下):

区域内容生信关注点
顶部仪表盘CPU 条(每核心一个)、内存条、swap条绿色=用户态,红色=内核态,蓝色=低优先级
进程列表PID、USER、CPU%、MEM%、COMMAND谁的CPU%最高?谁在大量读写?
底部快捷键F1-F10F6 排序、F9 kill、F5 树形视图

1.2 生信必备 htop 操作#

Terminal window
# 启动时就按CPU排序
htop -s PERCENT_CPU
# 只看某个用户的进程
htop -u bioinfo
# 树形视图看父子进程关系
# 在htop里按 F5,或者启动时:
htop -t
# 只显示特定进程(配合 -p)
htop -p $(pgrep -d, bwa) # 只看bwa的所有进程

树形视图在生信中的妙用:当你用 GNU Parallel 或 Snakemake 提交了几十个任务,树形视图能一眼看出哪个主进程卡住了多少子进程。

1.3 htop 里的颜色条解读#

对于生信工作负载,最常见的模式:

  • 全是绿色(用户态CPU):正常——BWA/HISAT2 等计算密集型
  • 大量红色(内核态CPU):⚠ 大量系统调用或 I/O 等待——可能是磁盘瓶颈
  • 灰色(I/O Wait):⚠⚠ 进程在等磁盘——你的 RAID 阵列或 NFS 跟不上
  • 蓝绿色(低优先级 nice):有人在后台跑 low-priority 任务

如果看到持续高 I/O Wait(>20%),问题不在 CPU 而在磁盘,接着看 iotop。

1.4 命令行快速采样#

Terminal window
# 一次性快照(类似top -bn1但更漂亮)
htop -C # 无颜色单色模式,适合重定向到文件
# 批量采样
for i in {1..10}; do
echo "=== Sample $i at $(date) ==="
ps aux --sort=-%cpu | head -6
sleep 5
done > cpu_samples.log

2. 内存监控——OOM之前的黄金5分钟#

2.1 free——第一眼看内存#

Terminal window
free -h

输出解读:

total used free shared buff/cache available
Mem: 503Gi 198Gi 5.2Gi 2.1Gi 299Gi 300Gi
Swap: 32Gi 12Gi 20Gi

生信重点看 available 不是 free!Linux 会大量使用内存做缓存(buff/cache),free 列看起来很少但 available 才是真正可分配给新进程的。

memavailable=memfree+reclaimable(buff/cache)mem_{available} = mem_{free} + reclaimable(buff/cache)

2.2 进程内存排名#

Terminal window
# 按内存使用排序前10
ps aux --sort=-%mem | head -11
# 只看RSS(物理内存),更准确
ps -eo pid,user,rss,comm --sort=-rss | head -20 | \
awk '{printf "%-8s %-10s %-12s %s\n", $1, $2, sprintf("%.1fG", $3/1048576), $4}'
# 或者用更直观的
ps aux | awk '{print $6/1024 " MB\t" $11}' | sort -rn | head -10

2.3 实时监控内存的脚本#

#!/bin/bash
# monitor_mem.sh —— 当可用内存低于阈值时告警
THRESHOLD_GB=20
INTERVAL=30
while true; do
available_kb=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
available_gb=$((available_kb / 1048576))
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [[ ${available_gb} -lt ${THRESHOLD_GB} ]]; then
echo "[${timestamp}] ⚠ WARNING: Only ${available_gb}GB available!" | tee -a mem_alerts.log
# 显示内存使用TOP5
echo "Top 5 memory consumers:" >> mem_alerts.log
ps aux --sort=-%mem | head -6 >> mem_alerts.log
else
echo "[${timestamp}] OK: ${available_gb}GB available"
fi
sleep ${INTERVAL}
done

2.4 OOM Killer 日志分析#

Terminal window
# 查看OOM事件
dmesg | grep -i "killed process" | tail -20
# 或者
journalctl -k | grep -i oom | tail -20
# 典型输出:
# Out of memory: Killed process 12345 (bwa) total-vm:64GB, anon-rss:58GB

生信预防OOM的经验

  1. 估算每个进程的内存:BWA mem 约需 参考基因组大小 × 1.5 + reads大小 的内存
  2. 限制并发数 = 总可用内存 / 单进程峰值内存
  3. 给关键系统留 10-20GB 缓冲
  4. 跑大任务前 free -h 确认

3. 磁盘IO监控——BAM写入的隐形瓶颈#

生信最容易被忽略的瓶颈是磁盘 I/O。比对本身是 CPU 密集,但读写 FASTQ 和 BAM 才是真正的吞吐量杀手。

3.1 iostat——IO吞吐概览#

Terminal window
# 安装
sudo apt install sysstat -y
# 持续监控(每秒刷新)
iostat -x 1
# 只看某个设备
iostat -x -p sda 1

关注列:

含义健康值危险值
r/s w/s每秒读写次数<1000>5000 (随机IO瓶颈)
rkB/s wkB/s每秒读写KB取决于磁盘接近磁盘标称值
%util磁盘繁忙度<70%>90%(持续)
await平均IO等待(ms)<10ms(SSD)>50ms
aqu-sz平均队列长度<5>20

生信典型场景的 iostat 解读:

IOthroughput=BAMsizetwriteIO_{throughput} = \frac{BAM_{size}}{t_{write}}

一个 50GB 的 BAM,如果 wkB/s 稳定在 200MB/s,大约需要 250 秒。如果 %util 已经到了 98% 但 wkB/s 只有 50MB/s——说明你在用机械硬盘,考虑换 SSD 或用 /dev/shm 做临时目录。

3.2 iotop——谁在疯狂读写#

Terminal window
sudo apt install iotop -y
sudo iotop -o # -o 只显示有IO活动的进程

iotop 直接告诉你:哪个进程在读写、读了多少、写了多少、I/O 占用百分比。

Terminal window
# 生信典型输出:
# PID DISK READ DISK WRITE COMMAND
# 12345 200M/s 180M/s bwa mem
# 12346 0M/s 150M/s samtools sort

看到 samtools sort 大量写入临时文件时,考虑:

Terminal window
# 把临时目录放到内存(/dev/shm)或快速SSD
export TMPDIR=/dev/shm
samtools sort -T /dev/shm/sort_tmp -@ 16 input.bam -o output.bam

3.3 磁盘空间——du和ncdu#

Terminal window
# 查看各目录的空间占用
du -h --max-depth=2 /opt/bioinfo | sort -rh | head -20
# 只看大于1GB的文件
find /data -type f -size +1G -exec ls -lh {} \; 2>/dev/null | awk '{print $5, $NF}'
# ncdu —— 交互式磁盘分析(超级推荐!)
sudo apt install ncdu -y
ncdu /data

ncdu 可以按 d 删除、按 n 按名称排序、按 s 按大小排序。找到谁把磁盘吃光了,按一下 d 直接删。

4. 长期监控——nmon记录一切#

跑一个 48 小时的流程,过程中不可能一直盯着屏幕。nmon 能记录所有指标到文件,事后分析。

Terminal window
sudo apt install nmon -y
# 每30秒采集一次,持续24小时(2880个快照)
nmon -f -s 30 -c 2880 -m /opt/logs/nmon/
# -f: 输出到文件(默认命名 hostname_YYYYMMDD_HHMM.nmon)
# -s 30: 每30秒采样
# -c 2880: 采样2880次
# -m: 输出目录
# 用nmonchart把.nmon转成图表
# 或直接用 nmon 打开 .nmon 文件:
nmon -r /opt/logs/nmon/server_20250101_1200.nmon

分析nmon数据的关键指标#

在 nmon 文件中按对应键查看:

按键内容生信用途
cCPU看计算密集型步骤的CPU利用率
m内存有没有内存泄漏(持续增长)
d磁盘读写带宽、IOPS
n网络NFS/数据传输
t进程各进程资源占用排序

发现内存泄漏的nmon特征:内存 used 持续线性增长,即使进程数不变。对应地去找那个随运行时间增长内存的进程。

5. 生信实战:全流程监控脚本#

#!/bin/bash
# pipeline_monitor.sh —— 跑流程的同时记录资源
set -euo pipefail
LOG_DIR="./monitor_logs"
mkdir -p "${LOG_DIR}"
PREFIX="${LOG_DIR}/$(date +%Y%m%d_%H%M%S)"
# 启动后台监控
echo "Starting monitoring..."
# 1. CPU + 内存(每30秒)
(
echo "timestamp,cpu_user,cpu_sys,cpu_idle,cpu_wait,mem_total_kb,mem_avail_kb,swap_used_kb"
while true; do
ts=$(date +%s)
cpu_line=$(top -bn1 | grep "Cpu(s)" | sed 's/[,%]/ /g')
cpu_user=$(echo "$cpu_line" | awk '{print $2}')
cpu_sys=$(echo "$cpu_line" | awk '{print $4}')
cpu_idle=$(echo "$cpu_line" | awk '{print $8}')
cpu_wait=$(echo "$cpu_line" | awk '{print $10}')
mem_info=$(free -k | grep Mem:)
mem_total=$(echo "$mem_info" | awk '{print $2}')
mem_avail=$(echo "$mem_info" | awk '{print $7}')
swap_used=$(free -k | grep Swap: | awk '{print $3}')
echo "${ts},${cpu_user},${cpu_sys},${cpu_idle},${cpu_wait},${mem_total},${mem_avail},${swap_used}"
sleep 30
done
) > "${PREFIX}_cpu_mem.csv" &
MONITOR_PID=$!
# 2. 磁盘使用(每5分钟)
(
echo "timestamp,path,used_percent,avail_gb"
while true; do
ts=$(date +%s)
df -h /data /tmp / | tail -n +2 | while read -r fs size used avail pct mnt; do
echo "${ts},${mnt},${pct%\%},${avail}"
done
sleep 300
done
) > "${PREFIX}_disk.csv" &
DISK_PID=$!
# 3. 进程快照(每10分钟)
(
while true; do
echo "=== $(date) ==="
ps aux --sort=-%mem | head -16
echo ""
sleep 600
done
) > "${PREFIX}_processes.log" &
PROC_PID=$!
# ========== 主流程(替换成你的实际流程) ==========
echo "Starting main pipeline at $(date)" | tee "${PREFIX}_events.log"
# 这里放你的实际生信流程
# fastp -i ... -I ... -o ... -O ...
# bwa mem ... | samtools sort ...
# bcftools mpileup ...
echo "Pipeline finished at $(date)" | tee -a "${PREFIX}_events.log"
# ========== 停止监控 ==========
kill ${MONITOR_PID} ${DISK_PID} ${PROC_PID} 2>/dev/null || true
wait ${MONITOR_PID} ${DISK_PID} ${PROC_PID} 2>/dev/null || true
echo "Monitoring logs saved to: ${LOG_DIR}/"
echo " CPU/Memory: ${PREFIX}_cpu_mem.csv"
echo " Disk: ${PREFIX}_disk.csv"
echo " Processes: ${PREFIX}_processes.log"
echo " Events: ${PREFIX}_events.log"
# 快速汇总
echo ""
echo "=== Resource Summary ==="
echo "Peak memory usage:"
grep -v timestamp "${PREFIX}_cpu_mem.csv" | \
awk -F, '{avail=$7/1048576; if(max_used_mb=="") max_used_mb=($6-$7); if(($6-$7)>max_used_mb) max_used_mb=($6-$7)} END {printf " Max used: %.1f GB\n", max_used_mb/1048576}'
echo "Min available memory:"
grep -v timestamp "${PREFIX}_cpu_mem.csv" | \
awk -F, '{if(min_avail=="" || $7<min_avail) min_avail=$7} END {printf " Min avail: %.1f GB\n", min_avail/1048576}'

这个脚本在流程运行时后台持续记录所有资源指标。跑完后打开 CSV 用 Excel/R 画个图,给导师汇报时还能附上”计算资源消耗分析”。

6. 踩坑记录#

坑1:top%MEM 包含了共享内存#

症状:所有进程 %MEM 加起来远超 100%。

原因:top/ps%MEM 基于 RSS(常驻内存),而多个进程共享的库(如 libclibm)被重复计算。

Terminal window
# 更准确的物理内存占用:
smem -t -k # 需要安装 smem
# PSS (Proportional Set Size) 才是进程独有的内存

坑2:free 看到 available 很大但进程报 OOM#

症状:free -h 显示 100GB available,但程序还是 OOM 了。

原因:可能是 cgroup 限制(Docker/Slurm 等容器环境)。

Terminal window
# 检查 cgroup 限制
cat /sys/fs/cgroup/memory/memory.limit_in_bytes
# 如果是某个小于物理内存的值——找到罪魁祸首了

坑3:ncdu 扫描 / 根目录耗时太久#

症状:ncdu / 跑了 10 分钟还没完。

Terminal window
# 排除挂载的外部存储
ncdu -x / # -x: 不跨越文件系统边界
# 或先快速找大目录
du -h --max-depth=1 / 2>/dev/null | sort -rh | head -20
# 然后再 ncdu 进那个大目录

坑4:htop 里看不到所有 CPU 核心#

症状:128 核的服务器,htop 只显示了几行 CPU 条。

在 htop 里按 F2Display options → 勾选 Detailed CPU time,或者用 htop -C 把每个核显示为竖条。

其实另一个更简单的选择:

Terminal window
# 显示所有核心的负载
mpstat -P ALL 1

坑5:iostat%util 100% 不代表磁盘真满了#

症状:NVMe SSD 上 %util 显示 100%,但实际吞吐远没到标称值。

原因:%util 反映的是”至少有一个 IO 请求在处理的时间占比”。对于能并发处理的 NVMe,100% 不一定意味着饱和。更应该看 awaitaqu-sz。对于 NVMe:

real_saturationIOPScurrentIOPSrated×throughputcurrentthroughputratedreal\_saturation \approx \frac{IOPS_{current}}{IOPS_{rated}} \times \frac{throughput_{current}}{throughput_{rated}}

坑6:dudf 显示不一致#

症状:df 说磁盘满了,du 只统计出一半。

最常见原因:文件被删除但进程还持有文件句柄

Terminal window
# 找出那些"幽灵"文件
lsof +L1 | grep deleted
# 或者
lsof | grep '(deleted)'
# 重启持有这些句柄的进程就能释放空间

坑7:nmon 记录数据但不能实时看#

症状:nmon -f 只生成了文件,不知道怎么实时打开。

Terminal window
# 实时模式(不加 -f)
nmon
# 或者让 nmon 同时记录和显示
nmon -f -s 30 -c 2880 -m /opt/logs/ &
nmon -r /opt/logs/server_*.nmon # 打开正在写的文件
# 还有:把 nmon 数据转成网页图表
# nmonchart 可以把 .nmon 转成 .html

7. 总结#

监控目标首选工具一句话
谁吃了CPUhtop按F6排序CPU,F5看进程树
内存还剩多少free -h看available不是free
谁在吃内存ps aux --sort=-%mem前5名
磁盘IO谁在跑sudo iotop -o一目了然
磁盘用满了没df -h; ncdudf看全局, ncdu找元凶
长期趋势nmon -f跑完用nmonchart出图
文件被删但空间没释放lsof +L1找deleted文件

监控不是一次性工作,把它嵌到每个流程脚本里去。多花 5 行 Bash 写监控,可能省下 5 小时的调试时间——这是我崩了三台服务器后悟出来的道理。


本文于 2025-11-10 在 Debian 12 服务器上实测。htop 3.3.0, iotop 1.26, nmon 16q, sysstat 12.7.6。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

系统资源监控:htop/iotop/nmon/du
https://fg.ink/posts/system-monitoring-bioinfo/
作者
风观
发布于
2024-09-15
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
风观
风有来路,观有所思
分类
标签
站点统计
文章
50
分类
1
标签
29
总字数
61,837
运行时长
0
最后活动
0 天前

文章目录