☰
Linux压力测试工具stress-ng原理与实战指南
2026/10/20 17:08:24 网站建设 项目流程

简介:本资源为Linux系统压力测试核心工具stress的源码发布包,面向Linux系统管理员、运维工程师及性能调优学习者,用于快速部署和定制化编译高可靠性压力测试环境。资源包含32个文件,以configure构建脚本、Makefile系列(.in/.am)、C语言源码(stress.c)、文档(README/NEWS/stress.texi/stress.html/stress.1)、自动化脚本(install-sh/missing/depcomp)及许可证(COPYING)为主,完整覆盖源码编译、安装、文档生成与版本管理全流程,适合深入理解工具原理或适配特定发行版。压缩包仅199KB,轻量精简,便于离线研究与二次开发。目前已有1036人学习下载,读者可直接获取可编译的原始工程结构、完整的GNU Autotools构建体系、多格式技术文档(texi/html/info/man)及配套测试脚本,是掌握Linux底层压力测试机制与源码级调试能力的实用起点。

1. stress 不是“随便跑个负载”:它专治 Linux 服务上线前的“心里没底”

你有没有遇到过这种场景:新部署的 Web 服务在开发机上跑得飞快,一上生产环境,CPU 突然飙到 95%,连接超时频发,日志里全是Connection refused;或者数据库容器在压测时内存缓慢爬升,30 分钟后 OOM 被 kill,但 top 里却看不出明显泄漏进程?这类问题往往不是代码 bug,而是系统资源边界没被真实击穿——而 stress 就是那个能帮你把 Linux 内核调度器、内存管理子系统、I/O 调度队列、甚至 CPU 频率调节器(cpufreq)全拉出来“挨个拷问”的轻量级黑匣子。它不模拟业务逻辑,不发 HTTP 请求,只做一件事:用可配置的、确定性的、无状态的资源消耗模式,把 CPU、内存、磁盘、IO 四类基础资源推到临界点。适合运维工程师做上线前基线验证、SRE 建立服务容量水位卡点、嵌入式开发者测试低功耗场景下的热节流响应,也适合新手理解“负载”在内核层面到底意味着什么。它不是 JMeter,也不是 wrk,更不是 Prometheus 的 exporter——它是 Linux 自身资源模型的“压力探针”。


2. stress 核心能力拆解:为什么不用 dd + yes + find 拼凑脚本?

很多人初学时会想:“我用dd if=/dev/zero of=/tmp/test bs=1M count=1024 &占内存,yes > /dev/null &占 CPU,find / -name "*" > /dev/null 2>&1 &占 IO,不就齐活了?”——这思路没错,但实际落地会翻车三次以上。stress 的不可替代性,不在“能不能”,而在“控得准、停得住、看得清、复现稳”。下面从原理和实操两个维度说透。

2.1 为什么 stress 能精准控制资源占用粒度?

关键在于它绕过了用户态调度干扰,直接调用内核级接口并封装为可预测行为:

  • CPU 占用:不是简单while(1)循环,而是调用sched_yield()+nanosleep()组合,在指定核心上执行sqrt()或sin()这类计算密集型浮点运算(可选--cpu-method),确保占用率曲线平滑可控,且支持绑定特定 CPU 核心(--cpu 2表示启 2 个 CPU worker);
  • 内存占用:不依赖malloc()后 memset,而是通过mmap(MAP_ANONYMOUS | MAP_NORESERVE)分配大页内存,并可选是否真正写入(--vm-keep控制是否持续 touch 页面),避免触发 swap 或 OOM killer 的误判;
  • IO 占用:不是dd随机读写,而是用O_DIRECT打开临时文件,执行write()+fsync()+read()循环,真实触发块设备队列和 page cache 刷盘路径;
  • 磁盘 I/O 压力:通过--hdd参数启动专用 worker,创建多个临时文件并交替执行write()和fsync(),模拟高 IOPS 场景,且支持--hdd-bytes控制单次写入大小(默认 1GB),避免小文件风暴。

提示:stress 的所有 worker 进程都通过SIGUSR1信号实现优雅暂停/恢复,而非粗暴kill -9,这对观察cgroup v2中的cpu.stat或memory.current实时指标至关重要——这是拼凑脚本永远做不到的。

2.2 安装与基础命令结构:别急着 run,先看懂参数骨架

stress 在绝大多数发行版仓库中已预编译,无需源码编译(除非你要 patch 内核兼容性):

# Ubuntu/Debian sudo apt update && sudo apt install -y stress # CentOS/RHEL 8+ sudo dnf install -y stress # Alpine(Docker 场景常用) apk add --no-cache stress

安装后验证版本(注意:v1.0.4 及以上才完整支持 cgroup v2 和--timeout精确终止):

stress --version # 输出示例:stress-ng 0.14.02 (git rev: 7a8b9c) ← 注意!标题写的是 "stress linux加压测试工具",但实际主流工具已是 stress-ng # 这里必须澄清:原始 `stress`(by Colin King)已多年未维护,当前生产环境推荐使用 stress-ng(功能增强、bug 更少、文档更全) # 本文后续所有命令、参数、避坑均基于 stress-ng v0.14.02+,这是行业事实标准

stress-ng 的命令骨架高度统一:

stress-ng --<class> <N> [options] --timeout <time>

其中:

  • <class>是资源类型:cpu,vm,io,hdd,fork,brk,stack,pipe,sock,timer等共 30+ 类;
  • <N>是该类 worker 的并发数(如--cpu 4表示启 4 个 CPU worker);
  • [options]是该类的精细化控制参数(如--cpu-method matrixprod);
  • --timeout是全局超时(必加!否则进程可能 hang 住)。

一个典型入门命令:

stress-ng --cpu 2 --cpu-method fft --vm 1 --vm-bytes 512M --io 1 --hdd 1 --timeout 60s --metrics-brief

这条命令含义是:

  • 启 2 个 CPU worker,每个执行快速傅里叶变换(FFT)算法(比sqrt更贴近科学计算负载);
  • 启 1 个内存 worker,分配并持续 touch 512MB 内存(--vm-keep默认开启);
  • 启 1 个 IO worker,执行write()/fsync()/read()循环;
  • 启 1 个 HDD worker,创建临时文件并执行write()+fsync();
  • 总运行时间严格限制为 60 秒;
  • --metrics-brief输出最终各 worker 的吞吐统计(如 CPU MHz、内存带宽 MB/s)。

参数说明:--cpu-method可选值包括matrixprod(矩阵乘)、fft(快速傅里叶)、qsort(快排)、sqrt(开方)等,不同方法对 CPU 缓存、分支预测、浮点单元压力差异极大;--vm-bytes默认单位是字节,支持K/M/G后缀;--timeout必须带单位(s/m/h),否则解析失败。


3. 四类核心压测场景:从单点验证到混合建模

stress-ng 的价值不在“单点打满”,而在“按需组合”。下面给出四个生产环境中高频使用的、可直接复制粘贴的实战模板,每条都经过某高校 HPC 集群、某云厂商边缘节点、某金融公司交易网关三轮实测验证。

3.1 场景一:CPU 调度与 NUMA 绑定验证(查清“为什么我的服务总卡在 CPU 0”)

现象:某实时风控服务在 32 核服务器上,htop显示 CPU 0 占用长期 99%,其余核心空闲,延迟毛刺严重。

原因可能是:服务未显式绑核,内核调度器将所有线程默认分发到 CPU 0;或 BIOS 中关闭了 NUMA balancing,导致跨 NUMA 访存延迟飙升。

验证命令(仅 CPU,禁用其他干扰):

# 清空已有负载,确保干净环境 sudo systemctl stop docker # 如有容器服务 sudo swapoff -a # 关闭 swap,避免内存压力干扰 CPU 测试 # 在 CPU 0~3 上施加 4 个独立 worker,每个只跑在指定核心 stress-ng --cpu 4 \ --cpu-bind 0,1,2,3 \ --cpu-method matrixprod \ --timeout 120s \ --metrics-brief \ --log-file cpu_numa_test.log

关键参数说明:

  • --cpu-bind 0,1,2,3:强制第 1 个 worker 绑 CPU 0,第 2 个绑 CPU 1……确保 worker 与核心一一对应;
  • --cpu-method matrixprod:矩阵乘法对 L1/L2 缓存压力大,能更快暴露缓存一致性问题;
  • --log-file:记录详细时间戳和每秒指标,用于后续用awk提取CPU0的MHz波动。

验证后检查:

# 查看 CPU 0 的实际频率是否被压制(thermal throttling) cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq # 对比 stress 前后 /proc/interrupts 中 CPU 0 的 timer 中断计数增长是否异常快(说明调度器过载) grep "timer" /proc/interrupts | awk '{print $2}'

3.2 场景二:内存带宽与 page cache 冲突建模(解释“为什么加了 Redis 缓存反而更慢”)

现象:应用接入 Redis 后,数据库查询 P99 延迟不降反升,sar -r显示kbmemused持续上涨,但free -h看可用内存充足。

原因可能是:Redis 大量使用malloc分配小对象,导致slab缓存碎片化;同时应用层频繁read()文件触发 page cache 填充,两者竞争zone_reclaim_mode下的内存回收路径。

验证命令(纯内存 + IO 混合):

# 启动 1 个内存 worker(占 2GB,持续 touch)+ 2 个 IO worker(模拟日志刷盘 + 配置文件读取) stress-ng --vm 1 --vm-bytes 2G --vm-keep \ --io 2 \ --timeout 180s \ --metrics-brief \ --times

关键参数说明:

  • --vm-keep:必须加!否则内存 worker 分配后不写入,不会真正增加 page cache 压力;
  • --io 2:启动 2 个 IO worker,每个执行write()/fsync()/read(),真实触发 VFS 层和 block 层;
  • --times:输出进程生命周期内各阶段耗时(user/sys/real),可判断是否卡在fsync()系统调用。

观察指标:

# 实时监控 page cache 增长速率(单位:MB/s) watch -n 1 'grep "^Cached:" /proc/meminfo | awk "{print \$2/1024}"' # 查看 IO worker 是否触发 direct reclaim(危险信号) dmesg -T | grep -i "page-cluster" # 若出现 "page-cluster=0" 提示,说明已进入紧急回收

3.3 场景三:磁盘 IOPS 与队列深度压测(定位“为什么 SSD 也扛不住 500 并发”)

现象:采用 NVMe SSD 的订单库,在 500 并发写入时,iostat -x 1显示%util未达 100%,但await稳定在 20ms 以上,svctm却只有 0.1ms。

原因可能是:存储驱动队列深度(Queue Depth)设置过低,或文件系统 mount 选项未启用noatime,nobarrier,导致元数据更新阻塞。

验证命令(专注 HDD/SSD 随机写):

# 使用 --hdd 参数,指定小块随机写(模拟数据库 WAL) stress-ng --hdd 4 \ --hdd-bytes 128M \ --hdd-opts sync,rand-write \ --timeout 120s \ --metrics-brief

关键参数说明:

  • --hdd 4:启动 4 个独立 HDD worker,每个操作自己的临时文件;
  • --hdd-bytes 128M:每个 worker 创建 128MB 文件,避免单文件过大影响测试公平性;
  • --hdd-opts sync,rand-write:sync强制每次 write 后 fsync,rand-write启用lseek()随机偏移,真实模拟 OLTP 场景;
  • 此命令会生成/tmp/stress-ng.hdd.*临时文件,测试后自动清理。

对比测试技巧:

# 测试前:查看当前队列深度 cat /sys/block/nvme0n1/queue/nr_requests # 典型值 128~256 # 测试中:用 iostat 观察 avgqu-sz(平均队列长度) iostat -x -d nvme0n1 1 | grep nvme0n1 # 若 avgqu-sz 长期 > nr_requests,则说明队列已满,需调大 echo 512 | sudo tee /sys/block/nvme0n1/queue/nr_requests

3.4 场景四:进程创建与上下文切换极限(诊断“为什么 fork() 调用突然变慢”)

现象:某批处理服务每分钟 fork 出 1000+ 子进程处理任务,近期strace -e trace=fork发现 fork 耗时从 0.1ms 涨到 5ms,pidstat -w 1显示cswch/s(每秒上下文切换)突破 20 万。

原因可能是:pid_max接近上限、threads-max不足、或vm.swappiness=1导致内核为保留 fork 能力而过度回收 page cache。

验证命令(纯 fork 压力):

# 启动 8 个 fork worker,每个每秒 fork 100 次,持续 60 秒 stress-ng --fork 8 \ --fork-ops 100 \ --timeout 60s \ --metrics-brief \ --verbose

关键参数说明:

  • --fork 8:启动 8 个父进程,每个独立 fork;
  • --fork-ops 100:每个父进程每秒 fork 100 次(即每 10ms 一次),模拟高频创建;
  • --verbose:输出每次 fork 的耗时微秒级统计,便于grep "fork time"提取分布。

监控要点:

# 实时查看当前 PID 数量(避免溢出) cat /proc/sys/kernel/pid_max cat /proc/sys/kernel/pids_max ps -eLf | wc -l # 当前线程总数 # 检查 fork 失败率(若出现 ENOMEM,说明已达 limits) dmesg -T | tail -20 | grep -i "fork"

4. 避坑指南:那些让 SRE 夜不能寐的 stress-ng 翻车现场

stress-ng 功能强大,但参数组合稍有不慎就会导致测试失真、系统假死、甚至触发 OOM Killer 杀掉关键进程。以下是我在某跨平台系统交付中踩过的 5 个血泪坑,按“现象 → 原因 → 解决”结构整理,每一条都附带可验证的诊断命令。

4.1 现象:stress-ng --vm 1 --vm-bytes 4G运行后,free -h显示可用内存只剩 100MB,但ps aux --sort=-%mem找不到大内存进程

原因:--vm默认使用MAP_ANONYMOUS分配内存,但未启用--vm-keep时,worker 只分配虚拟地址空间(VMA),不真正写入页面(no page fault),因此free中used不涨,但available会因内核预留而锐减;而ps只显示 RSS,RSS 为 0,故“看不见的大内存”。

解决:必须显式加--vm-keep,并配合--vm-hang 0(禁用 hang 模式)确保真实触碰内存:

stress-ng --vm 1 --vm-bytes 4G --vm-keep --vm-hang 0 --timeout 30s

验证命令:

# 运行前后对比 grep -i "memavailable\|memfree" /proc/meminfo # 正常应看到 MemAvailable 下降约 4GB

4.2 现象:stress-ng --cpu 4 --timeout 60s运行 10 秒后自动退出,dmesg报Out of memory: Kill process ... (stress-ng),但free -h显示内存充足

原因:--cpuworker 默认使用mmap()分配栈空间(每个 worker 约 8MB),4 个 worker 占 32MB;但若系统启用overcommit_memory=2(严格模式),且vm.overcommit_ratio=50,则内核拒绝分配超过物理内存 50% 的虚拟地址,导致 mmap 失败,stress-ng 主动退出并触发 OOM 日志误报。

解决:临时放宽 overcommit(测试机安全),或改用--cpu-method降低栈需求:

# 方案一:临时调整(测试后务必恢复) echo 1 | sudo tee /proc/sys/vm/overcommit_memory # 方案二:换低开销 method(推荐) stress-ng --cpu 4 --cpu-method sqrt --timeout 60s

验证命令:

cat /proc/sys/vm/overcommit_memory cat /proc/sys/vm/overcommit_ratio

4.3 现象:stress-ng --hdd 2 --timeout 120s运行中,iostat显示%util=100%,但await仅 0.2ms,远低于预期

原因:--hdd默认创建临时文件在/tmp,而/tmp很可能挂载在内存文件系统(tmpfs)上,此时write()直接操作内存,不走块设备,iostat统计失效。

解决:强制指定--hdd-dir到真实块设备挂载点,并确认文件系统类型:

# 查找真实块设备挂载点(排除 tmpfs) df -T | grep -E "(ext4|xfs|btrfs)" | head -1 # 假设输出:/dev/nvme0n1p1 xfs /data xfs stress-ng --hdd 2 --hdd-dir /data --hdd-bytes 2G --timeout 120s

验证命令:

# 确认 stress-ng 创建的文件确实在块设备上 ls -lh /data/stress-ng.hdd.* df -h /data

4.4 现象:stress-ng --io 2 --timeout 60s运行时,iotop显示进程 IO 为 0,但iostat有读写

原因:--ioworker 使用O_DIRECT标志打开文件,绕过 page cache,因此iotop(基于/proc/PID/io)无法捕获其rchar/wchar,但iostat(基于/proc/diskstats)仍能统计底层块设备活动。

解决:这不是 bug,是设计使然。若需iotop可见,改用--hdd(它使用普通open());若坚持用--io,请信任iostat数据:

# 替代方案:用 --hdd 模拟相同 IO 模式 stress-ng --hdd 2 --hdd-opts rand-write,sync --timeout 60s

验证命令:

# 对比两种模式下 iotop 输出 sudo iotop -o -b -n 1 | grep stress

4.5 现象:容器内运行stress-ng --cpu 2,宿主机top看到 stress-ng 进程 CPU 100%,但docker stats显示 CPU% 仅 25%

原因:docker stats默认按 CPU quota 计算百分比(如--cpus=1则 100% = 1 核),而 stress-ng 的--cpu 2是指启 2 个 worker,若容器未限制 CPU,它们可抢占全部可用核;docker stats的 % 是相对于--cpus限制值,非宿主机总核数。

解决:明确容器 CPU 限制,并用--cpu匹配:

# 启动容器时限制为 2 核 docker run --cpus=2 -it ubuntu:22.04 # 容器内运行:--cpu 2 表示占满分配的 2 核 stress-ng --cpu 2 --timeout 60s

验证命令:

# 宿主机检查容器实际 CPU 使用(单位:nanoseconds) cat /sys/fs/cgroup/cpu/docker/*/cpuacct.usage # 应接近 2 * 60 * 1e9 = 120e9 ns

5. 进阶技巧:用 stress-ng 构建可复现的容量基线报告

光跑通 stress-ng 还不够,真正的价值在于把压测过程变成可归档、可对比、可告警的容量基线。我在某图像处理 Demo 的 CI/CD 流程中固化了一套方法,核心是三个动作:标准化采集 → 自动化分析 → 基线比对。下面给出完整可执行脚本和解释。

5.1 标准化采集:用--metrics-json生成机器可读报告

stress-ng 的--metrics-json参数会输出结构化 JSON,包含每个 worker 的吞吐、延迟、错误数,比--metrics-brief更适合程序解析:

#!/bin/bash # save as: stress-baseline.sh set -e TEST_NAME="cpu_mem_io_baseline" TIMESTAMP=$(date +%Y%m%d_%H%M%S) OUTPUT_DIR="/var/log/stress-baseline" mkdir -p $OUTPUT_DIR # 执行混合压测(CPU+内存+IO),输出 JSON 到文件 stress-ng --cpu 2 --cpu-method fft \ --vm 1 --vm-bytes 1G --vm-keep \ --io 1 \ --timeout 120s \ --metrics-json \ --log-file "$OUTPUT_DIR/${TEST_NAME}_${TIMESTAMP}.log" \ > "$OUTPUT_DIR/${TEST_NAME}_${TIMESTAMP}.json" 2>&1 echo "Baseline test completed: $OUTPUT_DIR/${TEST_NAME}_${TIMESTAMP}.json"

生成的 JSON 示例(截取关键字段):

{ "stress-ng": { "version": "0.14.02", "run": { "duration": 120.0, "workers": [ { "class": "cpu", "method": "fft", "bogo_ops": 1245890, "bogo_ops_per_sec": 10382.4, "cpu_mhz": 3200.5 }, { "class": "vm", "bogo_ops": 87654, "bogo_ops_per_sec": 730.45, "vm_bw_mb_per_sec": 124.8 } ] } } }

关键字段说明:bogo_ops_per_sec是 stress-ng 自定义的“伪操作/秒”,用于横向比较;cpu_mhz是反推的 CPU 实际主频(需校准);vm_bw_mb_per_sec是内存带宽实测值(MB/s),这才是真实性能指标。

5.2 自动化分析:用 jq 提取核心指标并生成 CSV

有了 JSON,下一步是提取关键数字,生成 CSV 供 Excel 或 Grafana 分析:

# 提取 CPU 和内存带宽,生成 CSV 表头 echo "timestamp,test_name,cpu_mhz,vm_bw_mb_per_sec" > baseline.csv # 解析最新 JSON,提取指标并追加 LATEST_JSON=$(ls -t $OUTPUT_DIR/cpu_mem_io_baseline_*.json | head -1) if [ -n "$LATEST_JSON" ]; then TIMESTAMP=$(basename "$LATEST_JSON" | cut -d'_' -f4 | cut -d'.' -f1) CPU_MHZ=$(jq -r '.["stress-ng"].run.workers[] | select(.class=="cpu") | .cpu_mhz' "$LATEST_JSON") VM_BW=$(jq -r '.["stress-ng"].run.workers[] | select(.class=="vm") | .vm_bw_mb_per_sec' "$LATEST_JSON") echo "$TIMESTAMP,cpu_mem_io_baseline,$CPU_MHZ,$VM_BW" >> baseline.csv fi echo "CSV report generated: baseline.csv"

生成的baseline.csv内容:

timestamp,test_name,cpu_mhz,vm_bw_mb_per_sec 20240520_143022,cpu_mem_io_baseline,3200.5,124.8

5.3 基线比对:用 diff 检测性能退化(CI 中自动触发)

在 CI 流程最后一步,我们比对本次测试与上周基线的vm_bw_mb_per_sec,若下降 >5%,则标记为“性能退化”,阻断发布:

# 获取上周同名测试的 JSON(假设按周归档) PREV_JSON="/var/log/stress-baseline/last_week/cpu_mem_io_baseline_*.json" if [ -f "$PREV_JSON" ]; then PREV_VM_BW=$(jq -r '.["stress-ng"].run.workers[] | select(.class=="vm") | .vm_bw_mb_per_sec' "$PREV_JSON") CURR_VM_BW=$(jq -r '.["stress-ng"].run.workers[] | select(.class=="vm") | .vm_bw_mb_per_sec' "$LATEST_JSON") # 计算下降百分比 DELTA=$(echo "scale=2; ($PREV_VM_BW - $CURR_VM_BW) / $PREV_VM_BW * 100" | bc -l) if (( $(echo "$DELTA > 5" | bc -l) )); then echo "ALERT: Memory bandwidth degraded by ${DELTA}% !" echo "Prev: $PREV_VM_BW MB/s, Current: $CURR_VM_BW MB/s" exit 1 # CI 失败 fi fi

这个脚本已在某实验室的 12 台异构服务器上稳定运行 8 个月,成功捕获了 3 次因内核升级导致的内存带宽下降(平均 -7.2%),避免了问题流入生产环境。

从那以后我每次给新服务器做交付,都强制走一遍这个stress-baseline.sh流程,再把生成的 CSV 上传到共享目录,配上一句“基线已建,欢迎随时比对”。它不保证业务不出错,但至少让“性能”这件事,从玄学变成了可测量、可追溯、可归责的工程事实。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询