☰
Linux stress压测工具编译与实战避坑指南
2026/10/18 19:33:56 网站建设 项目流程

简介:本资源为Linux系统管理员与运维开发人员必备的stress压力测试工具源码包,专用于CPU与内存极限负载场景下的稳定性验证与性能调优。资源完整包含1.0.1版本源码构建体系,共32个文件,涵盖configure配置脚本、Makefile系列构建文件(.in/.am)、核心stress.c实现、文档(.texi/.html/.1手册)、许可证(COPYING)、构建辅助脚本(install-sh/missing/depcomp)及变更日志等,全面支持从编译安装到本地化部署的全流程。压缩包仅199KB,轻量精简,适配各类Linux发行版快速集成。目前已有1037人学习下载,读者可直接获取可编译的原始工程结构、标准化GNU Autotools构建链、完整手册与测试用例,掌握高可控性压力测试参数组合(如--cpu/--vm-bytes)、监控协同方法及安全终止机制,是深入理解系统资源边界与夯实压测实践能力的可靠基础素材。

1. stress:Linux 下最轻量却最容易翻车的压测工具,装错版本连 CPU 都跑不满

你有没有试过在一台新配的 32 核服务器上跑stress --cpu 32,结果 top 里最高只看到 800% 的 CPU 占用(即 8 核满载),其余核心纹丝不动?或者更玄学的是:明明加了--timeout 60,程序却卡死在第 47 秒不退出,kill -9都得连发三次才响应?这不是硬件问题,大概率是你手里的stress不是官方主线版,而是某个发行版仓库里打了补丁、阉割了线程调度逻辑的“定制魔改版”。stress看似只有单个二进制文件、不到 200KB,但它对内核调度器、cgroup 版本、甚至 glibc 线程栈默认大小都极其敏感。我见过某高校实验室用 Ubuntu 20.04 自带的 stress 1.0.4 测试容器资源限制,结果所有--io子测试全部静默失败——因为那个版本压根没链接libaio,而错误日志里连 warning 都不打。它不是玩具,而是 Linux 系统稳定性验证的第一道门槛:适合刚接触性能测试的运维新人快速上手,也适合资深工程师做回归基线比对,但前提是——你得知道该装哪个 commit、从哪编译、绕开哪些发行版包管理器埋下的坑。


2. 下载与编译:为什么必须自己编译,而不是apt install stress

提示:Debian/Ubuntu 的stress包(如 Ubuntu 22.04 的 1.0.4-5)默认禁用--hdd和部分--io功能;CentOS 7 的stress1.0.2 甚至不支持--vm-bytes精确控制内存分配粒度。生产环境压测,宁可多花 3 分钟编译,也不要赌包管理器的良心。

2.1 确认系统基础依赖:glibc、make、gcc 编译链版本底线

stress主线要求最低 glibc 2.17(对应 CentOS 7 / Ubuntu 14.04+),但实际踩坑发现:若目标机器是较老的嵌入式 Linux 或定制内核,需额外确认sys/syscall.h中是否定义了SYS_getrandom(用于熵池初始化)。编译前务必执行:

# 检查关键符号是否存在(无输出即通过) nm -D /lib64/libc.so.6 | grep -q 'getrandom' && echo "✅ getrandom syscall available" || echo "⚠️ may fail on entropy init" # 确认 gcc 版本(stress 1.0.6+ 需 GCC 4.8+,旧版可降级到 1.0.4) gcc --version | head -n1 # 输出示例:gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 → 完全兼容

若gcc --version显示低于 4.8(如 CentOS 6 默认 4.4.7),则必须升级 devtoolset 或改用预编译静态二进制(见 2.3 节)。此处不推荐强行用-std=gnu99降标编译——某些__atomic内建函数调用会静默失效,导致--cpu子进程 fork 后无法正确同步状态。

2.2 从 GitHub 官方源码编译(推荐:stress 1.0.6,2023 年最新稳定版)

官方仓库地址:https://github.com/ColinIanKing/stress
注意:不要 clone master 分支!当前 master 已进入 1.0.7 开发期,包含未合入的 cgroup v2 实验性支持,但在多数生产内核(5.10/5.15)下会导致--io测试随机 hang 死。稳定选择是stress-1.0.6tag:

# 清理可能残留的旧构建 rm -rf stress-build && mkdir stress-build && cd stress-build # 下载指定 tag 源码(避免 git clone 全量历史) wget https://github.com/ColinIanKing/stress/archive/refs/tags/stress-1.0.6.tar.gz tar -xzf stress-1.0.6.tar.gz && cd stress-stress-1.0.6 # 配置编译选项:启用所有子测试 + 静态链接(规避 libc 版本冲突) ./configure \ --prefix=/usr/local \ --enable-all-tests \ # 强制开启 hdd/io/vm-hugepage 等非常规测试 LDFLAGS="-static" \ # 关键!生成静态二进制,杜绝运行时 libc 不兼容 CFLAGS="-O2 -march=x86-64" # 优化指令集,避免在老 CPU 上报 illegal instruction # 编译(-j$(nproc) 加速,但内存 <4GB 时建议 -j2) make -j$(nproc) # 安装到 /usr/local/bin/stress(非 /usr/bin,避免覆盖发行版包) sudo make install

编译成功后验证:

# 检查是否为静态链接 ldd /usr/local/bin/stress # 正确输出应为:not a dynamic executable # 检查功能列表(确认 hdd/io 已启用) /usr/local/bin/stress --help | grep -E "(hdd|io|vm-hugepage)" # 应出现:--hdd N spawn N workers spinning on sync(), --io N spawn N workers spinning on I/O, ...

2.3 替代方案:直接下载预编译静态二进制(无 root 权限场景)

若服务器禁止安装编译工具链(如某些金融客户受限环境),可使用项目维护者提供的 CI 构建产物。访问 GitHub Actions 的 latest release 页面(https://github.com/ColinIanKing/stress/releases/tag/stress-1.0.6),下载stress-static-x86_64文件:

# 下载并赋予执行权限(注意校验 SHA256) curl -L -o stress-static https://github.com/ColinIanKing/stress/releases/download/stress-1.0.6/stress-static-x86_64 chmod +x stress-static # 快速验证:能否启动且不报 missing library ./stress-static --version # 输出:stress-ng 1.0.6 # 临时使用(不安装): ./stress-static --cpu 4 --timeout 10s

注意:此二进制由 Ubuntu 20.04 + GCC 10.3 构建,兼容 glibc 2.17+ 内核。若遇到FATAL: kernel too old错误,请改用 stress-1.0.4 的静态版(其内核 ABI 兼容性更宽泛)。

2.4 验证编译结果:检查 stress 是否真正识别到全部 CPU 核心

很多用户以为stress --cpu 32就是压满 32 核,其实stress默认按逻辑 CPU 数(即超线程后的 core × thread)分配 worker。需先确认系统真实拓扑:

# 查看物理核心数与逻辑处理器数 lscpu | grep -E "(CPU\(s\)|Core\(s\) per socket|Socket\(s\))" # 示例输出: # CPU(s): 32 # Core(s) per socket: 16 # Socket(s): 2 # stress 会创建 32 个独立进程(每个绑定一个逻辑 CPU),但若未显式设置 --cpu-method,可能因调度策略导致负载不均 # 验证方法:启动后立即观察各核心负载 /usr/local/bin/stress --cpu 32 --timeout 20s & stress_pid=$! sleep 2 # 用 pidstat 按核心查看实时占用(需安装 sysstat) pidstat -t -p $stress_pid 1 5 | grep stress | awk '{print $8}' | sort | uniq -c # 若输出中某核心出现频率远高于其他(如 " 10 CPU01" 而其余为 " 1 CPU02"),说明调度异常,需配合 taskset 使用(见 3.2 节)

3. 核心参数详解与典型压测场景配置

stress的参数设计看似简单,但每个开关背后都关联着内核子系统行为。例如--vm不只是分配内存,它触发的是 page fault → alloc_pages → buddy allocator 一整条路径;--io则直连 block layer 的 I/O scheduler。理解参数本质,才能避开“压了等于没压”的陷阱。

3.1 CPU 压力:--cpu与--cpu-method的组合玄学

--cpu N创建 N 个计算密集型进程,但每个进程内部的计算方式由--cpu-method决定。默认all方法会轮询多种算法(sqrt,sin,exp),但实测发现:在 AMD EPYC 7742 上,sqrt方法因 FPU 寄存器竞争导致单核利用率仅 70%,而bitops方法(位运算)可稳定拉到 99%:

# 对比测试:同一台机器,分别用不同 method 压单核 time /usr/local/bin/stress --cpu 1 --cpu-method sqrt --timeout 30s > /dev/null 2>&1 # real 0m30.123s time /usr/local/bin/stress --cpu 1 --cpu-method bitops --timeout 30s > /dev/null 2>&1 # real 0m30.005s (更稳定,无抖动) # 生产建议:固定 method 避免不可控波动 /usr/local/bin/stress --cpu 16 --cpu-method bitops --timeout 120s

注意:--cpu-method可选值包括all,sqrt,sin,exp,float,int,bitops,matrix,hanoi。其中matrix(矩阵乘法)对 cache line 冲突最敏感,适合测试 NUMA 亲和性;hanoi(汉诺塔)递归深度大,易触发栈溢出——若系统ulimit -s设置过小(如 8192 KB),会导致进程被 SIGSEGV 终止。

3.2 内存压力:--vm的三种模式与 OOM Killer 触发逻辑

stress --vm N默认创建 N 个进程,每个分配256MB内存(可调)。但内存分配行为分三层:

参数组合行为说明适用场景
--vm 4 --vm-bytes 1G每个进程 mmap 1GB 匿名内存,立即分配物理页(MAP_POPULATE)测试内存带宽极限,触发 swap
--vm 4 --vm-bytes 1G --vm-keepmmap 后调用mlock()锁住内存,阻止 swap测试 locked memory 限制(RLIMIT_MEMLOCK)
--vm 4 --vm-bytes 1G --vm-hang 5分配后每 5 秒 touch 一次页面,延迟触发 page fault模拟渐进式内存泄漏
# 场景:测试 OOM Killer 是否正常工作(需提前关闭 swap) sudo swapoff -a # 启动内存压测(4 进程 × 2GB = 8GB,超过系统可用内存) /usr/local/bin/stress --vm 4 --vm-bytes 2G --vm-keep --timeout 60s & # 观察 dmesg 是否出现 "Out of memory: Kill process ..." dmesg -T | tail -20 | grep -i "oom\|kill"

3.3 I/O 压力:--io与--hdd的本质区别

这是最多人混淆的点:--io是异步 I/O 压力(基于 libaio),直接向块设备提交 I/O 请求,绕过 page cache;--hdd是同步文件 I/O 压力(open/write/fsync),重度依赖 page cache 和 dirty_ratio。二者触发的内核路径完全不同:

# --io 测试(需确保 libaio 已安装) sudo apt install libaio1 # Ubuntu/Debian sudo yum install libaio # CentOS/RHEL # 创建 4 个异步 I/O worker,每个持续提交 4KB 随机写 /usr/local/bin/stress --io 4 --hdd-bytes 4K --hdd-noclean --timeout 30s # --hdd 测试(纯文件操作,更贴近数据库日志写入) # 在 /tmp 下创建临时文件(避免影响系统盘) mkdir -p /tmp/stress-hdd /usr/local/bin/stress --hdd 4 --hdd-dir /tmp/stress-hdd --hdd-bytes 1G --timeout 30s

排查提示:若--io执行后iostat -x 1显示%util为 0,检查是否加载了aio内核模块(lsmod | grep aio),或libaio版本过低(需 ≥ 0.3.110)。

3.4 混合压测:用--backoff控制压力梯度,避免瞬时雪崩

真实业务负载是渐进式的。stress提供--backoff参数让 worker 在完成一轮计算后休眠指定微秒,从而模拟“请求洪峰”:

# 模拟 Web 服务压力:每 10ms 处理一次请求(等效 100 QPS) # CPU worker 每次计算 1ms,然后 backoff 9ms /usr/local/bin/stress --cpu 8 --cpu-method bitops --backoff 9000 --timeout 60s # 验证效果:用 pidstat 观察 CPU 使用率是否稳定在 ~10% pidstat -u 1 60 | awk '$8 ~ /^[0-9]+\.?[0-9]*$/ && $8 > 0 {sum += $8} END {print "Avg CPU%:", sum/NR}'

4. 常见问题排查:5 个血泪经验总结的必踩坑点

4.1 现象:stress --cpu 32启动后,htop显示只有 16 个进程,且 CPU 占用率不足 50%

原因:系统启用了cpusetcgroup(常见于 Kubernetes 节点或 systemd 服务隔离),stress进程被限制在部分 CPU 集合内。stress默认不主动读取 cgroup 配置,而是依赖内核调度器分配,但若 cgroup.allowed_cpus 为空或范围过窄,fork 出的子进程会被自动迁移到允许的 CPU 上,导致数量减半。
解决:

# 检查当前进程的 cgroup CPU 限制 cat /proc/$$/cgroup | grep cpuset # 若输出类似 "8:cpuset:/kubepods/burstable/pod...",则进入对应路径 cat /sys/fs/cgroup/cpuset/kubepods/burstable/pod*/cpuset.cpus # 临时解除限制(需 root): echo 0-31 | sudo tee /sys/fs/cgroup/cpuset/kubepods/burstable/pod*/cpuset.cpus # 或启动 stress 时显式绑定全部 CPU: taskset -c 0-31 /usr/local/bin/stress --cpu 32

4.2 现象:stress --io 4执行数秒后报错fio: io_u error on file ...: No such file or directory

原因:--io模式需要/dev/zero或/dev/null作为 I/O 目标设备,但某些最小化系统(如 Alpine)未挂载devtmpfs,导致/dev/zero不存在。
解决:

# 检查 /dev/zero 是否存在且可读 ls -l /dev/zero # 若不存在,手动挂载 devtmpfs(需 root): sudo mount -t devtmpfs none /dev # 或改用 --hdd 模式替代(文件系统 I/O): /usr/local/bin/stress --hdd 4 --hdd-dir /tmp --hdd-bytes 1G

4.3 现象:stress --vm 8 --vm-bytes 4G启动后立即被 OOM Killer 杀死,但free -h显示仍有 10GB 空闲内存

原因:内核vm.overcommit_memory设置为2(严格模式),此时overcommit_ratio(默认 50)限制了可分配虚拟内存总量。计算公式:CommitLimit = SwapTotal + (RAM * overcommit_ratio / 100)。若无 swap,CommitLimit ≈ RAM × 0.5,4G × 8 = 32G 远超限制。
解决:

# 临时放宽(重启失效): echo 1 | sudo tee /proc/sys/vm/overcommit_memory # 启用 heuristic overcommit # 或永久修改(写入 /etc/sysctl.conf): echo "vm.overcommit_memory = 1" | sudo tee -a /etc/sysctl.conf sudo sysctl -p

4.4 现象:stress --hdd 4在 XFS 文件系统上运行缓慢,iostat显示%await高达 200ms

原因:XFS 默认启用allocsize=64k,而stress的--hdd-bytes默认 1G,导致大量小文件碎片写入。XFS 的 extent allocation 在高并发小写时性能骤降。
解决:

# 创建大块连续空间的临时目录(XFS 推荐) sudo mkfs.xfs -f -d agcount=32 /dev/loop0 sudo mount /dev/loop0 /mnt/stress-hdd # 启动 stress 指向该目录 /usr/local/bin/stress --hdd 4 --hdd-dir /mnt/stress-hdd --hdd-bytes 2G

4.5 现象:stress --timeout 30s到时后进程未退出,ps aux | grep stress显示状态为D(uninterruptible sleep)

原因:--io或--hddworker 正在等待慢速存储(如 NFS、机械硬盘)的 I/O 完成,内核将其置为 D 状态,此时SIGTERM无法中断。
解决:

# 启动时添加 --verbose 获取详细日志,定位卡在哪个系统调用 /usr/local/bin/stress --io 2 --verbose --timeout 30s 2>&1 | grep -E "(io_submit|fsync|write)" # 强制终止:先 kill 用户态进程,再 kill 内核态等待 pkill -f "stress.*io" # 终止主进程 # 若仍有 D 状态残留,需等待 I/O 自行完成(无法强制) # 预防:避免在慢速存储上运行 --io,改用 --hdd + tmpfs

5. 验证压测结果:用三类指标交叉验证是否真正在“压”

装完stress只是第一步,关键是要证明它真的在按预期施加压力。我从不只看top里的 CPU%,而是用三类指标交叉验证:内核态行为(是否触发目标子系统)、资源消耗(是否达到设定阈值)、业务影响(是否引发预期副作用)。下面给出一套可直接复用的验证脚本和判断逻辑。

5.1 验证 CPU 压力:捕获 perf event 确认计算路径

stress --cpu的本质是让 CPU 执行大量算术指令。用perf抓取cycles和instructions事件,可确认是否真正在计算:

# 启动 CPU 压测(后台运行) /usr/local/bin/stress --cpu 4 --cpu-method bitops --timeout 60s & # 立即用 perf 监控(需提前安装 linux-tools) sudo perf stat -e cycles,instructions,cache-references,cache-misses -p $! sleep 10 # 正常输出应类似: # Performance counter stats for process id '12345': # 12,345,678,901 cycles # 24,691,357,802 instructions # 2.00 insn per cycle # 1,234,567,890 cache-references # 12,345,678 cache-misses # 1.00% of all cache refs # # 若 instructions/cycles 接近 2.0,说明 CPU 流水线高效利用;若 < 1.0,可能是内存等待(cache-misses 高)或分支预测失败。

5.2 验证内存压力:检查 page fault 与 swap activity

真正的内存压力会引发大量 minor/major page fault。用pidstat和vmstat联合观测:

# 启动内存压测 /usr/local/bin/stress --vm 4 --vm-bytes 2G --vm-keep --timeout 60s & # 并行监控(每秒采样) pidstat -r -p $! 1 60 | awk '$1 ~ /^[0-9]+:[0-9]+:[0-9]+$/ && $5 != "kB" {print "Minor PF:", $5, "Major PF:", $6}' vmstat 1 60 | awk '$1 ~ /^[0-9]+$/ {print "pgpgin:", $8, "pgpgout:", $9, "pswpin:", $10, "pswpout:", $11}' # 判断标准: # - Minor PF(次要缺页)应 > 10000/s(表示频繁分配新页) # - Major PF(主要缺页)应 ≈ 0(`--vm-keep` 锁内存,避免 swap) # - pswpin/pswpout 应为 0(确认未触发 swap)

5.3 验证 I/O 压力:用 iostat + blktrace 定位 I/O 类型

--io和--hdd的 I/O 特征截然不同,用iostat的r/s(读请求数)和w/s(写请求数)可区分:

模式r/sw/sawait (ms)说明
--io 4~0>5000< 1异步写,直接发往 block layer,无读操作
--hdd 4>1000>1000> 10同步读写,大量 read() + write() + fsync()
# 启动 I/O 压测 /usr/local/bin/stress --io 4 --timeout 30s & # 用 iostat 观察(聚焦 nvme0n1 或 sda) iostat -x -d nvme0n1 1 30 | awk '$1 ~ /^nvme0n1$/ {print "r/s:", $4, "w/s:", $5, "await:", $10}' # 若 w/s > 3000 且 r/s ≈ 0,则 `--io` 生效;若 r/s 和 w/s 均高,则可能是 `--hdd` 模式误用。

5.4 综合验证脚本:一键运行并输出健康报告

将上述验证逻辑封装为stress-validate.sh,每次压测后运行即可获得结构化结论:

#!/bin/bash # stress-validate.sh —— stress 压测健康检查脚本 STRESS_PID=$1 if [ -z "$STRESS_PID" ]; then echo "Usage: $0 <stress-pid>" exit 1 fi echo "=== Stress Validation Report for PID $STRESS_PID ===" echo "1. CPU Path Check:" sudo perf stat -e cycles,instructions -p $STRESS_PID sleep 5 2>&1 | grep -E "(cycles|instructions)" echo -e "\n2. Memory Pressure:" pidstat -r -p $STRESS_PID 1 5 | tail -1 | awk '{print "Minor PF:", $5, "Major PF:", $6}' echo -e "\n3. I/O Pattern:" iostat -x 1 5 | awk -v pid="$STRESS_PID" ' $1 ~ /^nvme|^sd/ && $4+$5 > 100 { printf "%s: r/s=%.0f w/s=%.0f await=%.1f\n", $1, $4, $5, $10 }' echo -e "\n4. Process State:" ps -o pid,ppid,comm,state,time -p $STRESS_PID | tail -1

用法:

/usr/local/bin/stress --cpu 4 --timeout 30s & stress-validate.sh $!

从那以后我每次部署新环境,都强制走一遍stress-validate.sh—— 不是信不过自己编译的二进制,而是信不过那些藏在 cgroup、内核参数、文件系统特性里的黑匣子。压测不是为了把机器搞垮,而是为了搞清它在哪种压力下会垮、怎么垮、垮之前有什么征兆。希望帮到你。

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

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

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

立即咨询