编写Bash脚本统计3个进程1分钟磁盘读取量:读取值恒为0求助
问题分析与解决办法
核心问题原因
你之前的思路错误在于直接累加/proc/$pid/io中的read_bytes值——这个字段是进程从启动到当前的累计读取字节数,不是每秒的增量。如果进程在监控期间没有读取操作,每次读取的数值都一样,累加后相当于初始值重复加60次;如果初始值为0且进程无读写行为,结果自然是0。另外,如果进程提前退出,/proc/$pid/io会消失,grep返回空后awk输出0,也会导致结果异常。
正确实现思路
- 先记录每个目标进程的初始read_bytes值、PID和启动命令行;
- 持续监控60秒,期间可每秒检查进程状态(避免进程退出后报错);
- 60秒后读取每个进程的最终read_bytes值,用最终值减去初始值,得到该进程1分钟内的实际读取字节数;
- 按要求格式输出结果。
完整脚本示例
#!/bin/bash # 定义要监控的三个进程(可替换为具体PID或进程名) # 若用进程名,需确保每个名称唯一匹配单个进程,否则调整pgrep逻辑 TARGET_PROCESSES=("nginx" "mysql" "php-fpm") # 存储进程初始数据:格式为 "PID:COMMAND:INIT_READ" declare -a PROC_DATA # 采集初始数据 for proc_name in "${TARGET_PROCESSES[@]}"; do pid=$(pgrep -o "$proc_name") if [ -z "$pid" ]; then echo "警告:未找到进程 $proc_name" continue fi # 处理/proc/cmdline的null分隔符,转为正常命令行格式 cmd=$(cat /proc/$pid/cmdline | tr '\0' ' ') init_read=$(grep "read_bytes:" /proc/$pid/io 2>/dev/null | awk '{print $2}') if [ -z "$init_read" ]; then echo "警告:无法获取进程 $pid 的初始读取数据" continue fi PROC_DATA+=("$pid:$cmd:$init_read") done # 监控60秒 end_time=$((SECONDS + 60)) while [ $SECONDS -lt $end_time ]; do sleep 1 # 可选:此处可添加进程存活检查,提前标记退出的进程 done # 计算最终读取量并输出 echo "PID:启动命令行:读取数据量" for data in "${PROC_DATA[@]}"; do pid=$(echo "$data" | cut -d: -f1) cmd=$(echo "$data" | cut -d: -f2-) init_read=$(echo "$data" | cut -d: -f3) final_read=$(grep "read_bytes:" /proc/$pid/io 2>/dev/null | awk '{print $2}') if [ -z "$final_read" ]; then echo "$pid:$cmd:进程已退出" continue fi total_read=$((final_read - init_read)) echo "$pid:$cmd:$total_read" done
关键细节说明
- 进程匹配:脚本用
pgrep -o获取进程名对应的首个PID,若已知具体PID,可直接把TARGET_PROCESSES改成PID列表,比如TARGET_PROCESSES=("1234" "5678" "9012"); - 命令行处理:
/proc/$pid/cmdline用null分隔字段,通过tr '\0' ' '转换为空格分隔的正常命令行; - 进程退出处理:若进程在监控期间退出,脚本会输出“进程已退出”,避免报错;
- 数值计算:用
final_read - init_read得到1分钟内的实际读取量,这是正确的统计方式,而非累加每秒的累计值。
测试验证
如果测试进程确实无读取操作,结果为0是正常的。可手动触发进程读取行为(比如用cat large_file作为测试进程),再运行脚本验证结果是否正确。
内容的提问来源于stack exchange,提问作者oselish
相关产品推荐
相关产品推荐

