简介:本资源为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加压测试工具:不是“跑个命令就完事”,而是摸清服务器真实承压边界的黑匣子
你有没有遇到过这样的场景:线上服务在低负载时稳如老狗,一到促销高峰或批量任务启动,CPU突然飙到95%、内存OOM被杀、磁盘I/O延迟暴涨到200ms以上,日志里却找不到明确瓶颈点?很多工程师第一反应是查top、看dmesg、翻应用日志——但这些全是“症状”。真正的问题往往藏在资源调度的临界区:比如4核CPU在持续100%满载下,第3个线程是否开始抢锁?内存带宽饱和时,NUMA节点间跨节点访问延迟是否翻倍?这些非线性退化行为,光靠监控指标根本抓不住。stress正是为这种场景而生:它不模拟业务逻辑,而是用可配置、可复现、可隔离的方式,对CPU、内存、IO、磁盘等子系统施加确定性压力,把服务器推到“将崩未崩”的边缘,从而暴露调度器缺陷、热节流阈值、驱动bug甚至硬件隐性故障。它适合运维工程师做上线前基线验证、SRE团队构建混沌工程压测链路、嵌入式开发者评估SoC散热余量——一句话:当你需要知道“这台机器到底能扛住什么”,而不是“现在它正在干什么”时,stress就是那个最轻量、最可靠、最不可替代的探针。
2. 从零跑通stress:最小命令、核心参数与四类压力源的精准控制
stress本身不依赖复杂环境,但它的威力完全取决于你是否理解每个参数背后的硬件语义。下面以实际压测目标为线索,拆解如何用最简命令达成具体效果。
2.1 安装与验证:三行命令确认环境可信度
# Ubuntu/Debian系(推荐用包管理器,避免手动编译引入版本差异) sudo apt update && sudo apt install -y stress # CentOS/RHEL系(注意:EPEL源需提前启用) sudo yum install -y epel-release && sudo yum install -y stress # 验证安装并查看内置帮助(关键!stress --help输出即权威文档) stress --version stress --help | head -n 20提示:
stress --version输出应为stress 1.0.4或更高(当前主流发行版仓库中多为1.0.4)。若版本过低(如1.0.2),其--hdd-bytes参数可能不支持G单位,会导致后续磁盘压测失败——这是新手踩坑高频点,务必先验版本。
2.2 CPU压力:不是“占满”,而是“精准制造调度竞争”
单纯用stress --cpu 4会让4个进程各占一个CPU核心,看似满载,实则掩盖了真实瓶颈。生产环境中更危险的是多线程争抢同一核心或缓存一致性风暴。正确做法是:
# 场景:模拟高并发Web服务,8个线程争抢4个物理核心(触发上下文切换与缓存失效) stress --cpu 8 --timeout 60s --verbose # 关键参数解析: # --cpu 8:启动8个CPU工作进程(每个进程执行sqrt(123456789)循环) # --timeout 60s:严格运行60秒后自动退出(避免忘记kill导致长驻) # --verbose:实时打印每个worker的PID、启动时间、已完成迭代数(用于事后比对CPU频率变化)逻辑说明:
--cpu N启动N个独立进程,每个进程在单个CPU核心上执行计算密集型任务。当N > 可用逻辑CPU数时,内核必须进行频繁上下文切换;当N远大于逻辑CPU数(如--cpu 32跑在8核机器上),L1/L2缓存会因频繁换入换出而失效,此时perf stat -e cache-misses,cache-references可捕获到缓存未命中率飙升——这才是真实业务争抢资源的微观表现。
2.3 内存压力:避开OOM Killer的“温柔陷阱”
直接stress --vm 2(分配2GB内存)看似简单,但Linux默认启用overcommit,可能分配成功却无法真正写入,导致压测失效。必须强制触发实际内存占用:
# 场景:真实压测内存带宽与页回收机制(绕过overcommit欺骗) stress --vm 4 --vm-bytes 1G --vm-hang 0 --timeout 120s --verbose # 参数深挖: # --vm 4:启动4个内存worker进程 # --vm-bytes 1G:每个worker分配并**持续写入**1GB内存(关键!--vm-hang 0表示不休眠,持续刷脏页) # --vm-hang 0:禁用内存worker的休眠(默认是100ms,会导致压力断续) # --timeout 120s:确保压测持续足够时长,让kswapd和direct reclaim充分介入为什么
--vm-hang 0不可省略?因为stress的内存worker默认行为是:分配内存 → 写入随机数据 → 休眠100ms → 重复。这个休眠间隙会让内核有时间异步回收页,掩盖了瞬时内存压力峰值。设为0后,worker变成“永动机”,持续产生脏页,迫使内核立即触发direct reclaim,此时cat /proc/meminfo | grep -E "Active|Inactive|Swap"能清晰看到活跃内存页快速转为非活跃态,swap使用量缓慢上升——这才是内存压力的真实传导路径。
2.4 IO与磁盘压力:区分“吞吐压测”与“延迟压测”
很多人误以为stress --io 4就能测磁盘,其实它只创建4个同步写文件进程,对SSD几乎无感。要暴露IO栈瓶颈,必须组合参数:
# 场景1:测顺序写吞吐(模拟日志服务) stress --hdd 2 --hdd-bytes 4G --hdd-noclean --timeout 300s # 场景2:测随机读延迟(模拟数据库小文件查询) stress --hdd 4 --hdd-bytes 512M --hdd-rand --timeout 120s # 参数对照表: | 参数 | 作用 | 典型值示例 | 适用场景 | |------------------|----------------------------------------------------------------------|-----------------|------------------------| | `--hdd N` | 启动N个磁盘worker进程 | `--hdd 4` | 并发IO能力 | | `--hdd-bytes` | 每个worker操作的文件大小(必须带单位:B/K/M/G) | `--hdd-bytes 2G`| 控制单次IO负载量 | | `--hdd-rand` | 启用随机偏移读写(绕过page cache,直击磁盘) | (无值) | 测寻道延迟、IOPS | | `--hdd-noclean` | 压测后不删除临时文件(便于事后用iostat -x分析) | (无值) | 需要深度分析IO模式时 | | `--hdd-block-size`| 设置IO块大小(默认4K,调大可测吞吐,调小可测IOPS) | `--hdd-block-size 64K` | 精准匹配业务IO特征 |注意:
--hdd-rand是区分SSD与HDD的关键开关。对HDD,开启后iostat -x 1会显示await(平均IO等待时间)飙升至50ms+;对NVMe SSD,svctm(服务时间)仍稳定在0.1ms内,但%util可能达100%,此时需结合nvme smart-log看控制器温度与磨损——stress在这里成了硬件健康度的间接探测器。
3. stress与系统监控联动:用标准工具捕获压力下的真实退化信号
stress只是施压者,真正的洞察力来自它与Linux原生监控工具的组合。脱离监控的stress压测,就像蒙眼开车。
3.1 CPU层:用perf定位调度器瓶颈
当stress --cpu 16跑在8核机器上,仅看top显示CPU 100%,但无法判断是计算密集还是调度开销大。此时用perf抓取:
# 在stress运行期间,另起终端执行(需root权限) sudo perf record -e 'sched:sched_switch,sched:sched_migrate_task' -a -- sleep 30 # 分析调度事件频次(关键指标:每秒任务迁移次数) sudo perf script | awk '{print $3}' | sort | uniq -c | sort -nr | head -10逻辑说明:
sched:sched_switch记录每次上下文切换,sched:sched_migrate_task记录任务被迁移到其他CPU的事件。若输出中sched_migrate_task频次异常高(如>5000次/秒),说明负载均衡器正疯狂搬运线程,此时/proc/sys/kernel/sched_migration_cost_ns(默认500000ns)可能成为瓶颈,需调大该值减少误迁移——这是stress+perf才能发现的内核级调优点。
3.2 内存层:用bpftrace观测页回收链路
stress --vm 8 --vm-bytes 2G触发OOM前,内核会先尝试回收内存。传统free -h只能看结果,bpftrace可追踪全过程:
# 创建脚本 /tmp/page_reclaim.bt #!/usr/bin/env bpftrace kprobe:try_to_free_pages { printf("【页回收启动】zone=%s, order=%d, gfp_flags=0x%x\n", str(args->zone->name), args->order, args->gfp_mask); } kretprobe:try_to_free_pages /retval == 0/ { printf("【回收失败】需释放%d页,实际释放0页\n", (1 << args->order)); }# 执行监控(stress运行中) sudo bpftrace /tmp/page_reclaim.bt参数说明:
try_to_free_pages是内核页回收主函数。当retval == 0时,表示本次回收彻底失败,内核将进入OOM killer流程。此时args->order值(如3代表需释放8页)能反推当前内存碎片化程度——stress在此处变成了内存碎片的“CT扫描仪”。
3.3 IO层:用iostat + blktrace定位存储栈延迟
stress --hdd 4 --hdd-rand运行时,仅用iostat -x 1可能误判。需结合blktrace抓取底层请求:
# 开始blktrace(指定设备,如nvme0n1) sudo blktrace -d /dev/nvme0n1 -o nvme_trace & # 运行stress 60秒 stress --hdd 4 --hdd-bytes 1G --hdd-rand --timeout 60s # 停止trace并分析 sudo killall blktrace sudo blkparse -i nvme_trace | head -20 # 关键字段解读: # 1. Q:请求入队(Queue)时间戳 # 2. G:请求合并(Get)时间戳 # 3. M:请求重映射(Map)时间戳 # 4. I:请求插入IO调度器(Insert)时间戳 # 5. D:请求下发到设备(Dispatch)时间戳 # 若Q→I间隔长,说明调度器(如mq-deadline)排队严重;若I→D间隔长,说明设备驱动或固件响应慢血泪经验:某次压测中
iostat显示await=2ms(正常),但blkparse发现Q→I平均耗时15ms——根源是内核IO调度器队列长度被/sys/block/nvme0n1/queue/nr_requests限制为128,而stress并发4个worker持续发请求,队列溢出导致请求积压。调大该值后await降至0.3ms。没有blktrace,这个瓶颈永远藏在iostat的平均值之下。
4. stress压测避坑指南:5条血泪教训,每一条都曾让我重启服务器
stress看似简单,但参数组合稍有不慎就会导致系统假死、监控失真甚至硬件损伤。以下是我在多个生产环境踩过的坑,按“现象→原因→解决”结构整理:
4.1 现象:执行stress --cpu 32后,SSH连接卡死,但console仍可操作
原因:stress进程抢占所有CPU时间片,导致sshd进程无法获得调度机会;而Linux console(tty1)由内核直接驱动,不依赖用户态守护进程。
解决:永远添加--timeout参数,并用nice -n 19降低stress优先级:
nice -n 19 stress --cpu 32 --timeout 60s # 让sshd等关键进程能抢到CPU4.2 现象:stress --vm 4 --vm-bytes 8G执行后,free -h显示可用内存充足,但应用频繁OOM
原因:stress分配的内存被内核标记为MADV_DONTNEED,虽已分配但未真正驻留物理页;当应用申请内存时,内核需立即回收stress的页,触发同步reclaim,造成应用卡顿。
解决:强制stress立即写入并锁定内存:
stress --vm 4 --vm-bytes 8G --vm-keep --vm-hang 0 # --vm-keep确保内存不被swap,--vm-hang 0持续写入4.3 现象:stress --hdd 4压测SSD时,iostat显示%util=100%,但latency指标平稳,怀疑压测无效
原因:默认--hdd使用O_SYNC打开文件,但现代SSD固件会缓存sync写,导致iostat统计的%util虚高。
解决:强制直写设备,绕过所有缓存:
stress --hdd 4 --hdd-bytes 2G --hdd-direct # --hdd-direct等价于open(O_DIRECT)4.4 现象:在容器内运行stress --cpu 4,宿主机top显示CPU使用率仅20%,远低于预期
原因:容器默认使用CFS(Completely Fair Scheduler)配额,若未设置--cpus=4或cpu.cfs_quota_us,stress进程会被限频。
解决:容器启动时显式分配CPU资源:
docker run --cpus=4 -it ubuntu:22.04 bash -c "apt update && apt install -y stress && stress --cpu 4 --timeout 30s"4.5 现象:压测后系统时间跳变(date显示回拨数秒),NTP服务报警
原因:stress导致系统负载极高,内核时钟中断处理延迟,clock_gettime(CLOCK_MONOTONIC)返回值出现负向漂移。
解决:压测前禁用NTP时间调整,改用adjtimex微调:
sudo systemctl stop systemd-timesyncd sudo adjtimex -t 0.000001 # 将时钟漂移率设为极小值,避免压测中突变5. 进阶技巧:用stress构建可复现的混沌实验模板与自动化回归链路
stress的价值不仅在于单次压测,更在于将其嵌入CI/CD和混沌工程体系,形成可持续的稳定性保障闭环。我所在团队已将以下模式固化为标准实践。
5.1 构建“压力指纹”:用stress生成唯一系统基线
不同批次服务器即使型号相同,因固件版本、散热设计、BIOS设置差异,承压表现可能天差地别。我们为每台新购服务器生成“压力指纹”:
# 脚本 generate_fingerprint.sh #!/bin/bash SERVER_ID=$(hostname -s) DATE=$(date +%Y%m%d_%H%M%S) # 四维压力组合(覆盖CPU/内存/IO/磁盘) echo "=== Fingerprint for $SERVER_ID at $DATE ===" >> fingerprint_${SERVER_ID}.log echo "[CPU] 8-core saturation:" >> fingerprint_${SERVER_ID}.log stress --cpu 8 --timeout 30s --verbose 2>&1 | tail -5 >> fingerprint_${SERVER_ID}.log echo "[MEM] 4GB allocation rate:" >> fingerprint_${SERVER_ID}.log stress --vm 4 --vm-bytes 1G --vm-hang 0 --timeout 30s --verbose 2>&1 | tail -5 >> fingerprint_${SERVER_ID}.log echo "[IO] Random read IOPS:" >> fingerprint_${SERVER_ID}.log stress --hdd 4 --hdd-bytes 512M --hdd-rand --timeout 30s --verbose 2>&1 | tail -5 >> fingerprint_${SERVER_ID}.log # 生成后自动上传至内部CMDB scp fingerprint_${SERVER_ID}.log cmdb-server:/fingerprint/关键价值:当某台服务器在后续压测中
[MEM]环节比基线慢40%,无需排查应用,直接定位到内存条批次问题——这比任何监控告警都早72小时。
5.2 自动化回归:在GitLab CI中嵌入stress健康检查
我们将stress作为部署前必过关卡,防止配置变更引发隐性性能退化:
# .gitlab-ci.yml 片段 stability-test: stage: test image: ubuntu:22.04 before_script: - apt update && apt install -y stress sysstat script: # 1. 启动被测服务(此处为模拟的API服务) - python3 -m http.server 8000 & - sleep 5 # 2. 施加阶梯压力并采集指标 - for i in 2 4 8; do echo "Testing with $i CPU workers..." stress --cpu $i --timeout 10s & PID=$! # 采集10秒内服务P95延迟 timeout 10s bash -c 'for i in {1..50}; do curl -s -w "%{http_code}\n" -o /dev/null http://localhost:8000/; done' | \ awk '/200/{print NR}' | sort -n | awk 'NR==int(NR/2)+1' >> latency.log wait $PID done # 3. 验证延迟是否超阈值(P95 < 200ms) - awk '{sum+=$1} END {if (sum/NR > 200) exit 1}' latency.log artifacts: paths: - latency.log - fingerprint_*.log这个CI job会在每次代码合并前,自动对服务进行3轮压力递增测试,并将延迟数据存档。若某次提交导致
P95从150ms升至220ms,流水线直接失败——把性能退化拦截在上线前。
5.3 混沌工程集成:用stress触发K8s Pod驱逐策略验证
在Kubernetes集群中,我们利用stress模拟节点资源耗尽,验证Pod驱逐逻辑是否符合预期:
# chaos-stress-job.yaml apiVersion: batch/v1 kind: Job metadata: name: node-stress-chaos spec: template: spec: nodeSelector: kubernetes.io/os: linux tolerations: - key: "node.kubernetes.io/unreachable" operator: "Exists" effect: "NoExecute" containers: - name: stressor image: quay.io/brancz/stress:latest # 官方维护的stress镜像 command: ["/usr/bin/stress"] args: - "--cpu" - "16" - "--vm" - "4" - "--vm-bytes" - "4G" - "--timeout" - "180s" resources: requests: memory: "1Gi" cpu: "1000m" limits: memory: "8Gi" # 关键!设高limit触发kubelet OOMScoreAdj计算 cpu: "4000m" restartPolicy: Never核心逻辑:K8s kubelet根据
oom_score_adj值决定OOM时杀哪个Pod。通过resources.limits.memory设为8Gi,kubelet会将该Pod的oom_score_adj设为-999(最高优先级被杀),而业务Pod默认为-998。当stress耗尽节点内存,kubelet会先杀stress Pod,再杀业务Pod——这个逻辑必须通过真实stress触发才能验证。我们每周自动运行此Job,确保驱逐策略始终有效。
最后说句实在话:stress不是银弹,它不会告诉你“业务接口为什么慢”,但它会坚定地告诉你“这台机器的物理边界在哪”。我见过太多团队花两周调优JVM参数,却没花十分钟用stress --cpu 8 --timeout 30s确认CPU是否真的够用;也见过SRE同事为一个偶发的IO延迟抓耳挠腮,直到用stress --hdd-rand复现了磁盘固件bug。真正的稳定性,始于对硬件边界的敬畏,而非对监控曲线的迷信。希望帮到你。
本文还有配套的精品资源,点击获取