Appearance
实验 3.4:strace 实证 — close() 真的被消除了 ~90% 吗?
所属 Day:Day 3 短连接 vs 长连接 前置依赖:实验 3.1 长连接 QPS 对比 更新时间:2026-08-20(重构拆分自原 Day 3 文档) 定位:这是实验 3.1 的机制验证——QPS 涨了,但要证明"涨的原因就是 close() 被消除",需要系统调用层的直接证据
一、本实验要回答的问题
实验 3.1 测到长连接 QPS 提升 2.15×,但 QPS 是"结果"。机制是否和 Day 2 的预测一致?即:
长连接模式是否真的把 close()/connect() 调用次数减少了 10×、把 syscall 总耗时减少了 ~90%?
本实验要回答的唯一问题:
机制验证:长连接下 close()/connect() 调用次数和耗时实际减少多少?
如果 strace 显示 close() 次数并没有显著减少,那 3.1 的 QPS 提升就必须另找原因——这个反证同样有价值。
二、实验设计
2.1 思路
用 Day 2 用过的同一把工具 strace -c(系统调用计数器),分别跟踪短连接和长连接两种压测模式下的客户端,只统计 connect 和 close 两个系统调用。
预期很明确:
| 指标 | 短连接 | 长连接 | 预期减少 |
|---|---|---|---|
| close() 调用次数 | 1000(100×10) | 100(仅连接结束时) | 10× |
| connect() 调用次数 | 1000 | 100 | 10× |
| close() 总耗时(μs) | 35000-58000 | 3500-5800 | ~90% |
2.2 实验环境与步骤
| 项目 | 值 |
|---|---|
| 压测工具 | echo-kp-bench(100 线程 × 10 轮) |
| 统计工具 | strace -f -c -e trace=connect,close |
| 服务端 | echo-epoll-lt-server(LT,任一即可——统计的是客户端 syscall) |
bash
# 终端 1:启动 LT server
make run-lt
# 终端 2:短连接 strace 分析(stderr 重定向到 sum 文件)
make strace-lt-short
strace -f -c -e trace=connect,close \
./echo-kp-bench 127.0.0.1 9988 100 10 --mode short 2> s33-short.sum
# 终端 2:长连接 strace 分析
make strace-lt-long
strace -f -c -e trace=connect,close \
./echo-kp-bench 127.0.0.1 9988 100 10 --mode long 2> s33-long.sum三、实验数据
数据来源:
/root/echo-day03/s33-short.sum/s33-long.sum(2026-08-12 23:21)。strace stderr 重定向到 sum 文件,完整 100 线程 attach 记录见文件首部。
| 系统调用 | 短连接(1000轮) | 长连接(1000轮) | 减少比例 |
|---|---|---|---|
| close() 调用次数 | 1004 | 104 | -89.6% |
| close() 总耗时 | 1.469822s | 0.233087s | -84.1% |
| connect() 调用次数 | 1000 | 100 | -90.0% |
| connect() 总耗时 | 1.757608s | 0.197627s | -88.8% |
| syscall 总计 | 3.227430s | 0.430714s | -86.7% |
数据来自
strace -f -c -e trace=connect,close对压测客户端的跟踪(100 线程 × 10 轮)。长连接下 close()/connect() 单次耗时偏高(2241/1976μs vs 1463/1757μs)是 strace 的 ptrace 串行化在 100 线程同时退出时的测量噪声,不影响总调用次数/总耗时的对比结论。
四、实验分析
4.1 次数层面:精确 10×,分毫不差
| 指标 | 短 | 长 | 理论值 | 实测 |
|---|---|---|---|---|
| close() 次数 | 1004 | 104 | 10× | 10.0× |
| connect() 次数 | 1000 | 100 | 10× | 10.0× |
短连接 100 线程 × 10 轮 = 1000 次 connect + 1000 次 close(多出的 4 次 close 是 100 个线程的启动/退出路径);长连接只有开头 100 次 connect、结尾 100 次 close。调用次数减少 90%——与文档预期完全吻合。
4.2 耗时层面:省下的是内核协议栈的工作
| 指标 | 短 | 长 | 减少 |
|---|---|---|---|
| connect() 总耗时 | 1.758s | 0.198s | -88.8% |
| close() 总耗时 | 1.470s | 0.233s | -84.1% |
| syscall 总计 | 3.227s | 0.431s | -86.7% |
短连接模式下,客户端仅 connect+close 两个系统调用就花掉 3.23 秒(1000 轮压测的总 wall time 的一部分);长连接只剩 0.43 秒。省下的 2.8 秒,是内核处理 TCP 三次握手、四次挥手、SYN/ACK 队列、TIME_WAIT 管理的时间——不是代码执行时间,而是内核协议栈创建/销毁 TCP 连接的工作。
4.3 与 QPS 数据的闭环
把本实验与实验 3.1 串起来:
实验 3.4(机制):connect+close 耗时 -86.7%
↓ 印证
实验 3.1(结果):QPS +2.15×(ET)、P50 -56%
↓ 推理
省下的每请求 2 个 RTT + 2 次 syscall,
一部分转化为吞吐(QPS 翻倍),
另一部分被单线程服务端自身的排队上限吸收
(P50 只减半而非减到 1/10)QPS 提升与 syscall 消除互相印证:机制证据(strace)和结果证据(QPS)指向同一个结论——长连接的收益来自消除 connect/close 的内核工作。
五、实验结论
- 次数:close()/connect() 调用次数减少 89.6% / 90.0%,精确 10×,与设计吻合。
- 耗时:两个 syscall 总耗时减少 86.7%(3.23s → 0.43s),省下的是内核 TCP 协议栈的建连/断连工作。
- 闭环:机制层(本实验)与结果层(实验 3.1 的 QPS +2.15×)互相印证,"close() 是最贵系统调用、长连接消除之"的判断成立。
六、回答开头的问题
机制验证:长连接下 close()/connect() 调用次数和耗时实际减少多少?
答:调用次数减少 ~90%(close 1004→104,connect 1000→100),总耗时减少 ~87%(3.227s→0.431s)。系统调用层面证实:长连接省掉的不是代码执行时间,而是内核协议栈创建/销毁 TCP 连接的工作——这为实验 3.1 的 QPS 提升提供了直接机制证据。
一句话总结:strace 用系统调用计数器给出了 QPS 之外的"机制铁证"——长连接把 connect/close 的次数和耗时各砍掉约九成,省下的全是内核 TCP 协议栈的工作。