不能直接用 top 获取稳定、可解析的 CPU 占用率,因其依赖 TTY 且输出不统一;推荐读取 /proc/stat 计算差值,或使用 mpstat 等专为脚本设计的工具。

不能直接用 top 获取稳定、可解析的 CPU 占用率 —— 它是交互式终端程序,输出依赖 TTY,且默认会持续刷新,PHP 中调用会卡住或返回空。
为什么 shell_exec('top -bn1') 有时返回空或报错
top 在非终端环境下(如 PHP 的 CGI/FPM)常因缺少 TERM 环境变量或无法分配伪 TTY 而失败;即使加了 -bn1(batch mode + 1 次迭代),某些系统(如 Alpine Linux)的精简版 top 可能不支持该参数,或输出格式与预期不符(比如无 CPU 行、字段错位)。
- 检查是否真有
top:运行which top,部分容器镜像(如php:alpine)默认没装procps包 -
-bn1是必须的,但不是万能的:它只保证“一次快照”,不代表输出结构统一 - 避免用
exec('top -bn1 | head -n 5')直接截取 —— 不同系统top头部行数不同,CPU 行位置浮动
更可靠的方式:读取 /proc/stat 计算 CPU 使用率
Linux 内核通过 /proc/stat 暴露原始 CPU 时间戳(单位:jiffies),PHP 只需两次采样差值即可算出占用率,无需外部命令、无兼容性风险、开销极低。
- 第一行以
cpu开头的数据包含user、nice、system、idle、iowait等字段,总和即为总 CPU 时间 - 间隔 1 秒采样两次,用公式:
(total_jiffies_2 - total_jiffies_1 - idle_jiffies_2 + idle_jiffies_1) / (total_jiffies_2 - total_jiffies_1) - 示例片段(省略错误处理):
$stat1 = file_get_contents('/proc/stat');
preg_match('/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/m', $stat1, $m1);
list(, $u1, $n1, $s1, $i1) = $m1;
sleep(1);
$stat2 = file_get_contents('/proc/stat');
preg_match('/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/m', $stat2, $m2);
list(, $u2, $n2, $s2, $i2) = $m2;
$total1 = $u1 + $n1 + $s1 + $i1;
$total2 = $u2 + $n2 + $s2 + $i2;
$idle1 = $i1;
$idle2 = $i2;
$usage = round((($total2 - $total1) - ($idle2 - $idle1)) / ($total2 - $total1) * 100, 1);如果坚持用 shell 命令,优先选 mpstat 或 vmstat
mpstat(来自 sysstat 包)专为脚本设计,输出稳定、字段对齐,且支持单次采样:
立即学习“PHP免费学习笔记(深入)”;
- 安装:Debian/Ubuntu 上
apt install sysstat;Alpine 上apk add sysstat - 获取 1 秒内平均值:
mpstat 1 1 | awk 'NR==4 {print 100-$NF}'(第 4 行最后一列是 %idle) - 比
top更轻量,不依赖 TTY,输出格式长期稳定 - 注意:
mpstat默认可能禁用数据收集,首次运行前确认/etc/default/sysstat中ENABLED="true"并启动服务
真正难的不是“怎么拿到数字”,而是理解这个数字代表什么:它是采样窗口内的平均值,不是瞬时值;多核 CPU 下 top 显示的是所有核总和(可能超 100%),而 /proc/stat 和 mpstat 默认也是全局统计。若需单核数据,得解析 /proc/stat 中 cpu0、cpu1 等行,或用 mpstat -P ALL 1 1 —— 这些细节容易被忽略,但直接影响监控逻辑是否合理。



















