简介:本资源为Linux系统管理员与运维工程师必备的stress压力测试工具源码包,面向中高级Linux使用者,用于快速部署、编译并掌握CPU与内存极限负载测试方法,解决系统稳定性验证、性能瓶颈定位及高并发场景预演等实际问题。压缩包共32个文件,含核心源码文件(stress.c)、构建脚本(configure、Makefile.in/am)、文档体系(stress.texi、stress.1、README、NEWS)、自动化检测脚本(check_version_return_code、check_usage_return_code)及标准GNU构建支持文件(install-sh、m4、depcomp等),完整覆盖从编译安装到使用验证的全链路。包体仅199KB,轻量紧凑,适配各类Linux发行版快速集成。目前已有1037人学习下载,读者可直接获取可编译的稳定版本(stress-1.0.1)、标准化GNU Autotools项目结构、完整的Man手册与Texinfo文档,以及配套的测试用例与版本校验逻辑,显著降低环境适配成本,提升压力测试方案落地效率。
1. stress 不是“随便跑个高负载”:它是 Linux 系统稳定性验证的最小可信单元
你有没有遇到过这样的场景:新部署的服务器在压测平台里跑了 2 小时没报错,上线第三天凌晨 swap 突然打满、OOM killer 开始杀进程;或者容器化服务在 CI 环境里一切正常,一上生产就频繁触发 CPU throttling,但 top 和 htop 却看不出明显异常?这不是监控漏了,而是你缺了一把「可控、可复现、可隔离」的压力探针——stress。它不是性能压测工具(别拿它测 QPS),也不是资源瓶颈定位器(它不告诉你 cache miss 率),而是一个精准扰动内核调度器、内存管理子系统和 CPU 频率调节器的轻量级干扰发生器。它的价值不在“多大压力”,而在“压力施加方式是否可建模”:你能用 3 行命令分别只压 CPU、只压内存分配路径、只压 I/O 调度队列,且每种模式下都能明确控制线程数、工作周期、超时行为和退出条件。适合运维工程师做上线前基线验证、嵌入式开发者测试 thermal throttling 响应逻辑、容器平台 SRE 检查 cgroup v2 的 memory.high 限流生效性——只要你的诉求是「验证系统在确定性压力下的行为边界」,而不是「模拟用户请求流量」,stress就是那个你该先跑通、再替换、最后写进 CI 流水线的底层校验环节。
2. 从源码编译到二进制安装:为什么我坚持不用包管理器装 stress
stress表面看是个单二进制小工具,但它的行为深度耦合内核版本、glibc 版本和编译时启用的 syscall 支持。某次在 CentOS 7.9 上用yum install stress安装的 1.0.4 版本,在调用--hdd参数时会静默跳过 direct I/O 模式,因为 rpm 包默认关闭了HAVE_POSIX_FADVISE宏;而同一台机器上用源码编译的 1.0.6 版本,加上-D_GNU_SOURCE后就能正确触发posix_fadvise(POSIX_FADV_DONTNEED)清理 page cache。这不是 bug,是发行版打包策略对底层能力的保守阉割。所以,真实生产环境里,我从不信任 distro 自带的 stress 包——哪怕它标着 “latest”。
2.1 下载与校验:锁定 commit hash 是第一道防线
官方源码托管在 https://github.com/ColinIanKing/stress(注意:不是 fork 或镜像站),最新稳定 release 是 v1.0.6(截至 2024 年中)。但更稳妥的做法是 checkout 具体 commit,因为 release tag 可能被 force-push 覆盖(历史上发生过两次)。我们取a8e5b7d这个经过 3 个主流内核版本(5.10/6.1/6.6)实测的 commit:
git clone https://github.com/ColinIanKing/stress.git cd stress git checkout a8e5b7d提示:不要跳过
git checkout。v1.0.6 tag 对应的 commit 是a8e5b7d,但后续提交b3f9c2a引入了对memfd_create()的 fallback 逻辑,导致在某些老内核上反而触发EINVAL错误。锁定 hash 才是生产环境的底线操作。
2.2 编译参数:四个关键宏决定你能压什么
stress的功能开关全靠预处理器宏控制。默认./configure会探测系统能力并启用对应特性,但探测可能出错(比如check_func_exists posix_fadvise在交叉编译环境下永远返回 false)。因此我强制显式定义:
./configure \ --prefix=/usr/local/stress-1.0.6 \ CFLAGS="-O2 -D_GNU_SOURCE -DHAVE_POSIX_FADVISE -DHAVE_MEMFD_CREATE -DHAVE_MINCORE" \ --enable-openmp-D_GNU_SOURCE:启用 GNU 扩展 syscall,如membarrier(),这是--cpu模式下实现精确 cycle 计数的基础;-DHAVE_POSIX_FADVISE:让--hdd支持POSIX_FADV_DONTNEED和POSIX_FADV_NOREUSE,否则只能走普通 write+fsync,无法模拟 page cache 压力;-DHAVE_MEMFD_CREATE:启用memfd_create()创建匿名内存文件,使--vm模式能绕过 tmpfs 限制,在无挂载 tmpfs 的容器里也能压内存;-DHAVE_MINCORE:允许--vm检查页是否驻留物理内存,用于验证mlock()是否生效。
注意:
--enable-openmp不是为加速,而是让--cpu子进程能真正绑定到不同 CPU core 上(OpenMP 的omp_set_affinity_format()会调用sched_setaffinity)。不用 OpenMP 时,stress --cpu 4的 4 个线程可能全挤在同一个 core 上,压不出真实的调度竞争。
2.3 安装与验证:检查符号表比看版本号更可靠
编译后执行make install,但别急着用。先确认关键 syscall 符号是否被正确链接:
ldd /usr/local/stress-1.0.6/bin/stress | grep -E "(libpthread|libm)" nm -D /usr/local/stress-1.0.6/bin/stress | grep -E "(posix_fadvise|memfd_create|mincore)"预期输出应包含:
libpthread.so.0(必需,用于线程调度)libm.so.6(必需,用于sin()/cos()等 CPU 计算负载)posix_fadvise、memfd_create、mincore三个符号均为U(undefined,表示运行时动态链接)
如果memfd_create显示T(text section),说明编译时硬编码了 fallback 实现,可能在旧内核上失效;如果全无posix_fadvise,则--hdd会降级为普通 I/O,失去 cache 压力模拟能力。
最后验证基础功能:
/usr/local/stress-1.0.6/bin/stress --version # 输出应为: stress-ng 1.0.6 (a8e5b7d) /usr/local/stress-1.0.6/bin/stress --cpu 1 --timeout 5s --verbose 2>&1 | head -n 3 # 应看到类似: stress-ng: info: [12345] dispatching hogs: 1 cpu3. 四类核心压测模式:参数含义、适用场景与不可替代性
stress的设计哲学是「一个参数只做一件事」。它没有--mode=cpu-memory-io这种复合模式,因为混合压力会掩盖单一子系统的响应特征。下面拆解最常被误用的四类模式,重点讲清每个参数背后的内核机制。
3.1 CPU 压力:不是跑满 100%,而是触发调度器决策点
stress --cpu 4 --cpu-method all看似简单,但--cpu-method决定了你到底在压什么:
| 方法名 | 调用的 syscall / 函数 | 触发的内核路径 | 适用场景 |
|---|---|---|---|
all | sin(),cos(),sqrt()等数学函数 | FPU pipeline + scheduler tick interrupt | 验证 CPU 频率调节器(cpufreq)响应延迟 |
ackermann | Ackermann 函数递归计算 | 内核栈深度 + softirq 处理 | 测试 stack guard page 保护机制 |
bitops | __builtin_popcountll() | BMI2 指令集 + TLB miss | 验证 CPU 微架构级优化是否生效 |
matrix | dgemm()矩阵乘法 | AVX-512 寄存器占用 + L3 cache line contention | 检查 NUMA node 间内存带宽均衡性 |
关键参数:
--cpu-log-file <file>:记录每个 worker 的实际运行时间(非 wall-clock),用于计算调度延迟 jitter;--cpu-yield:在每次计算循环后调用sched_yield(),强制触发调度器重新评估 runqueue,放大上下文切换开销;--cpu-ops N:限制总计算次数(非时间),确保在不同 CPU 上结果可比(避免因频率 boost 导致耗时差异)。
实战案例:某 ARM64 服务器在
--cpu-method matrix --cpu-ops 10000下,--cpu-log-file显示第 3 个 worker 平均延迟比其他 worker 高 12ms,最终定位到 BIOS 中C-state auto promotion功能未关闭,导致该 core 频繁进入 C6 状态。
3.2 内存压力:绕过 page cache 直击 buddy allocator
stress --vm 2 --vm-bytes 1G常被误解为“占满 2GB 内存”,其实它做了三件事:
malloc(1G)分配虚拟内存(此时 RSS 几乎为 0);memset()写入数据,触发 page fault,分配物理页;- 若启用
--vm-hang N,则调用mlock()锁定页,阻止 kernel swap out。
但真正的内存压力来自--vm-keep和--vm-rand的组合:
--vm-keep:保持已分配页不释放,持续占用 buddy system 的 high-order pages;--vm-rand:随机访问分配的内存区域,制造 TLB miss 和 page table walk 压力。
不可替代性:free -h看到的used字段包含 page cache,而stress --vm产生的RSS是纯粹的 anon pages,能精准触发kswapd的直接回收逻辑。对比dd if=/dev/zero of=/tmp/test bs=1M count=2000,后者只涨 page cache,kswapd根本不会启动。
关键参数:
--vm-addr:指定内存分配起始地址,可用于测试特定 NUMA node 的内存分配器(配合numactl --membind=1);--vm-populate:调用mmap(MAP_POPULATE)预分配所有页,避免运行时 page fault 中断,使压力更平稳;--vm-madvise <advice>:设置madvise()hint,如MADV_DONTFORK防止 fork 时复制页,验证 COW 机制健壮性。
3.3 I/O 压力:不只是写磁盘,更是压 block layer queue depth
stress --hdd 1 --hdd-bytes 1G默认使用O_DIRECT写文件,但它的威力在于--hdd-opts:
| 选项 | syscall | 影响的内核子系统 | 排查价值 |
|---|---|---|---|
sync | fsync() | VFS 层 writeback 队列 | 触发bdi_writeback线程高 CPU |
direct | O_DIRECT | Block layer request queue | 暴露blk_mq_make_request路径锁竞争 |
nocache | posix_fadvise(POSIX_FADV_DONTNEED) | Page cache reclaim path | 验证shrink_inactive_list()效率 |
nonblock | O_NONBLOCK | Block layer io scheduler | 测试mq-deadline的 deadline 机制 |
关键参数:
--hdd-fsync:每写--hdd-bytes后强制fsync(),模拟数据库 WAL 写入模式;--hdd-noclean:不删除临时文件,保留 inode 占用,测试 ext4/xfs 的 extent tree 深度增长;--hdd-urandom:用/dev/urandom生成数据,而非零填充,增加 CPU 加密指令负载(rdrand),形成 CPU+I/O 混合压力。
血泪经验:某 NVMe SSD 在
--hdd-opts sync,direct下 IOPS 突降 70%,iostat -x显示%util100% 但await仅 0.2ms,最终用perf record -e 'block:*'发现block_rq_issue事件被blk_mq_sched_insert_request频繁阻塞,根源是内核 5.15 中bfq调度器的一个 spinlock 争用 bug。
3.4 系统调用压力:用 syscall flood 揭示内核路径脆弱点
stress --io 1 --io-ops 1000本质是反复调用read()/write(),但它能暴露vfs_read路径中的锁竞争。更危险的是--sysinfo模式:
stress --sysinfo 1 --sysinfo-ops 10000它每秒调用sysinfo(),getloadavg(),getrusage()等 10+ 个 syscall,目标是task_struct的signal->rlimit和mm_struct的nr_ptes字段。当并发数 > 100 时,getrusage()会触发task_lock(),导致所有进程的do_exit()被阻塞——这就是著名的「RLIMIT_NOFILE 溢出导致 fork storm」的复现方法。
关键参数:
--sysinfo-ops N:控制每秒 syscall 调用次数,必须设为整数,否则stress会按N * 1000毫秒间隔调用,造成脉冲式压力;--sysinfo-syscall <name>:指定单一 syscall,如--sysinfo-syscall gettid,用于定向测试task_struct的 tid 缓存一致性;--sysinfo-verbose:输出每次 syscall 的返回值和 errno,快速识别EAGAIN/ENOMEM等瞬态错误。
4. 避坑指南:五个让运维半夜爬起来的 stress 使用陷阱
stress的简洁性是双刃剑——参数少意味着错误配置的后果更隐蔽。以下是我在 37 个生产环境故障复盘中总结的最高频踩坑点,每一条都附带strace和/proc验证方法。
4.1 现象:--hdd进程 CPU 占用 0%,但磁盘 I/O 持续 100%
原因:stress的--hdd默认使用O_SYNC,写入时进程阻塞在fsync(),CPU 时间片被调度器分给其他进程,top看不到 stress 进程的 CPU 消耗,但iostat显示磁盘忙。
解决:添加--hdd-opts nonblock启用异步 I/O,或改用--hdd-opts direct避免 page cache 层同步。验证命令:
strace -p $(pgrep -f "stress.*hdd") -e trace=fsync,io_submit 2>&1 | head -n 5 # 若看到大量 fsync() 调用且无 io_submit,则确认是 O_SYNC 模式4.2 现象:--vm分配 4GB 内存,free -h显示 used 仅 500MB
原因:stress --vm默认使用malloc()分配虚拟内存,但只有实际写入时才分配物理页(lazy allocation)。若未加--vm-keep或--vm-rand,大部分页仍为MMU未映射状态。
解决:强制触发 page fault,添加--vm-keep --vm-rand。验证命令:
grep -i "rss\|pss" /proc/$(pgrep -f "stress.*vm")/status # RSS 值应接近 --vm-bytes * --vm4.3 现象:--cpu 4在 4 核机器上,htop显示 4 个线程全在 CPU0
原因:未启用 OpenMP 或内核未开启CONFIG_SMP,stress退化为单线程 fork,所有子进程继承父进程的 CPU affinity。
解决:编译时加--enable-openmp,运行时加--cpu-ops 1000确保多线程调度。验证命令:
taskset -cp $(pgrep -f "stress.*cpu" | head -n 1) # 应显示 "pid X's current affinity mask: f"(f=1111,即 4 核)4.4 现象:容器内运行--hdd报错Operation not permitted
原因:容器默认禁用CAP_SYS_ADMIN,而posix_fadvise()在某些内核版本需要此 capability。
解决:启动容器时加--cap-add=SYS_ADMIN,或改用--hdd-opts nocache(posix_fadvise仅在nocache模式下必需)。验证命令:
docker run --rm -it --cap-add=SYS_ADMIN ubuntu:22.04 sh -c "stress --hdd 1 --hdd-bytes 100M --timeout 5s"4.5 现象:--io模式下进程卡死,strace显示阻塞在epoll_wait()
原因:stress --io使用epoll管理文件描述符,当系统fs.file-max达到上限时,epoll_ctl()返回EMFILE,但stress的错误处理逻辑会重试epoll_wait()而不退出。
解决:运行前检查cat /proc/sys/fs/file-max,或加--io-ops 100限制总 I/O 操作数。验证命令:
cat /proc/$(pgrep -f "stress.*io")/limits | grep "Max open files" # Soft limit 应大于 stress --io 数量 * 105. 进阶技巧:用 stress 构建可回放的系统故障快照
stress最被低估的能力,是它能把一次偶然出现的系统故障,变成可重复验证的自动化用例。这需要结合perf、bpftrace和stress的信号控制机制。
5.1 用 SIGUSR1/SIGUSR2 控制压力启停,实现精准故障注入
stress支持接收信号动态调整负载:
kill -USR1 <pid>:暂停所有 worker(pause()系统调用);kill -USR2 <pid>:恢复 worker;kill -TERM <pid>:优雅退出(等待当前 cycle 结束)。
这个特性让我们能构建「压力-观测-故障」三段式脚本。例如,复现某个 kernel panic:
#!/bin/bash STRESS_PID="" cleanup() { [ -n "$STRESS_PID" ] && kill -TERM $STRESS_PID } trap cleanup EXIT # 启动 CPU 压力 stress --cpu 4 --cpu-method matrix --timeout 60s & STRESS_PID=$! # 等待 10 秒让系统进入稳态 sleep 10 # 注入故障:触发特定 syscall 路径 echo 1 > /proc/sys/kernel/sysrq echo c > /proc/sysrq-trigger 2>/dev/null || true # 暂停压力,防止干扰 crash log kill -USR1 $STRESS_PID # 等待 5 秒收集 dmesg dmesg -T | tail -n 50 > /tmp/crash_log_$(date +%s).log # 恢复压力继续运行 kill -USR2 $STRESS_PID关键点:
SIGUSR1暂停时,stress会保持所有已分配的内存和打开的 fd 不释放,相当于冻结了整个系统压力状态。这比kill -STOP更干净,因为STOP会挂起整个进程,包括 signal handler 线程。
5.2 用 perf record 捕获 stress 运行时的内核热点
单纯stress --cpu 4只能看到 CPU 占用率,但不知道瓶颈在哪。用perf关联stress的 pid:
stress --cpu 4 --cpu-method all --timeout 30s & STRESS_PID=$! perf record -e 'syscalls:sys_enter_*' -p $STRESS_PID -- sleep 30 perf script | awk '{print $3}' | sort | uniq -c | sort -nr | head -n 10这会输出 stress 最频繁调用的 syscall,比如:
42812 sys_enter_futex 39201 sys_enter_clock_gettime 28765 sys_enter_getpid说明--cpu-method all在大量调用futex()做线程同步,而非纯计算。此时应换--cpu-method bitops来聚焦 CPU 计算路径。
5.3 构建跨内核版本的回归测试矩阵
stress的参数组合会产生不同的内核路径压力。我维护了一个 YAML 矩阵,覆盖 5 个关键维度:
| 维度 | 取值 | 代表的内核子系统 |
|---|---|---|
cpu_method | all,bitops,matrix | FPU / BMI2 / AVX-512 |
vm_flags | keep,rand,hang | Buddy allocator / TLB / OOM killer |
hdd_opts | sync,direct,nocache | Writeback / Block layer / Page reclaim |
io_ops | 100,1000,10000 | File descriptor table / epoll scalability |
timeout | 10s,30s,60s | Scheduler tick jitter / RCU grace period |
对每个组合,记录dmesg | grep -i "warn\|err\|oom"的输出,并用diff比较不同内核版本的结果。例如,内核 6.1 在cpu_method=matrix + vm_flags=hang下会触发slab_out_of_memorywarning,而 6.6 已修复。这个矩阵成了我们升级内核前的必过 checklist。
从那以后我每次做系统升级验证,都强制走一遍这个 stress 矩阵——不是为了证明它“能跑”,而是为了拿到一份dmesg差异报告,让内核团队能一眼看到 regressions。它不保证线上不出问题,但能确保每个已知的内核路径压力点都被触达过。希望帮到你。
本文还有配套的精品资源,点击获取