首页 游戏 软件 排行 专题

下载大文件时 CPU 飙到90%,sendfile 却是关着的

来源: 本站整理 日期:2026/11/4 22:14:33

上个月有台 2 核 4G 的机器跑着静态下载站,白天访问量一般,晚上八点一放新版本,客户端就反馈下载速度上不去。远程上去一看就明白了:Nginx 的 worker 进程 CPU 常年占着 90% 以上,网卡却只跑了两三 Mbps,机器带宽是 100M 的,等于花了钱买了根细水管。查了半天才发现,问题出在一个再普通不过的配置上sendfile off;。这台机器是别人交付时用 apt 装的默认 nginx 模板,配置文件里明明白白写着 sendfile off,理由是"怕 sendfile 传大文件把磁盘打满"。就是这一行,让每次下载都要让 CPU 替内核搬两遍数据。

大文件下载要的就是「网卡跑满、CPU 闲着」:vollcloud(香港 CMIN2 大带宽线路,吞吐量可达 5G/s) 走的是 CMIN2 回程直连,管道本身就够宽;如果是流量大、想按月买断而不是卡峰值峰值的场景,profitserver(全球多地 VPS,不限流量) 这种不限流量的机器也可以先拿来做对比测试。不过话说回来,线路再宽,配置不对一样跑不满带宽下面这几步才是正事。

一、先确认 CPU 是被谁吃掉的

别一看到 CPU 高就往数据库、PHP 身上猜。下载站上最直观的做法是分时段看:白天低峰记录一次 top,晚高峰下载时再记录一次,然后对比两个进程。

如果两次都看到 nginx: worker process 排在最前面,且 CPU 占用跟并发下载数成正比每多一个下载连接,worker 就多占一截那基本可以断定是文件传输路径在吃 CPU,而不是业务逻辑。再用 ps -o pid,ppid,pcpu,cmd -C nginx 确认这些 worker 是不是同一个 master 派生的。

第二步是把系统调用抓出来。看 sendfile 有没有用上,最直接的工具是 strace:

strace -f -p <worker_pid> -e trace=read,write,sendfile,sendto,writev -c

跑几十秒,然后 Ctrl+C 看统计。如果统计里 read 和 write 的调用次数跟传输的数据量对得上,read 和 write 交替出现,而 sendfile 是 0,那就实锤了:Nginx 走的是「用户态读 → 用户态写」的老路,零拷贝压根没启用。

服务器通过网络线缆向客户端传输大文件数据包的概念图

二、为什么 sendfile 关着就会「网卡空转」

要理解这件事,得知道内核在传文件时有两条路。

第一条路是 read + write。Nginx 从磁盘把文件读进自己的用户态缓冲区(这一步发生一次内存拷贝),再通过 socket 写出去(又发生一次内存拷贝,还伴随一次用户态到内核态的上下文切换)。文件越大,搬的次数越多,CPU 消耗是线性增长的,而且这个过程是同步的一个 worker 搬数据的时候,没法同时去干别的活。

第二条路是 sendfile。这是 Linux 2.0.6 以后提供的系统调用,作用是把"把文件内容送到 socket"这件事整体交给内核:磁盘页缓存里的数据直接挂到 socket 缓冲区上,中间不经过用户态。数据从头到尾只在内核空间里移动,**零拷贝**,CPU 只负责发起调用和搬动页指针,不负责一个字节一个字节地复制。

所以 sendfile 关掉之后,下载大文件时的表现就是:CPU 被复制动作吃满、内存带宽被白白占用两次、系统调用数量暴涨,而网卡因为数据是被"喂"进去的,喂的速度就是 CPU 的复制速度,永远跑不满。你的带宽是 100M,网卡最多跑到 20M,瓶颈根本不在网络上。

这里还有个容易被误解的点:很多人以为 sendfile 是"传输加速"魔法。实际上它不改变单个连接的绝对吞吐上限,它改变的是单位 CPU 能搬多少数据。在高并发下载(几十上百个连接同时拉)的时候差距会被放大到夸张的地步同样一台 2 核机器,关掉 sendfile 最多撑住 30 个并发大文件下载,打开之后能扛住 200 个还稳稳的。

零拷贝传输路径示意图:数据从磁盘经内核页缓存直接进入网卡

三、该开哪些参数,一共四行

先看最常见的静态文件下载场景,这四行基本是标配:

sendfile on;
tcp_nopush on;
output_buffers 4 256k;
keepalive_timeout 65;

sendfile on; 是主角,打开零拷贝。tcp_nopush on; 配合它工作,作用是让内核把攒够的数据包尽量一次性推出去,减少小包对网络的干扰,把带宽利用率提上去。两者一起用是官方推荐组合。

output_buffers 是缓冲区数量和大小,机器内存小就调小(2 个 128k),内存富裕就调大,能进一步减少 worker 之间的等待。

如果你的机器是 NVMe 盘、文件特别大(比如几 GB 的安装包、影视文件),还可以再上 directio,绕开页缓存直接从磁盘读:

directio 4m;
aio threads;

directio 的块大小要跟文件系统和硬件对齐,常见值是 4m 或 8m。这里必须提醒一句:directio 和 sendfile 在静态文件场景下是互相拆台的sendfile 是从页缓存里搬,directio 是绕开页缓存直接读,Nginx 里两者对同一个 location 是排斥的,你想要零拷贝就用 sendfile,想要大文件直读就单独开 directio location,别指望它俩叠加。另外 directio 只对大于块大小的文件生效,小文件照旧走缓存。

运维人员查看 CPU 占用率监控界面排查下载瓶颈

四、三个容易踩的连带问题

问题一:sendfile 打开后,gzip 压缩还灵不灵?很多老资料说 sendfile 和 gzip 冲突,开启 sendfile 后静态文件就不再压缩了。实际上现在的 Nginx 版本里,gzip 过滤器仍在链路中生效,压缩照做,只是压缩本身要吃 CPU,跟 sendfile 是两件事。真要既压缩又高效,用 gzip_static on; 让 Nginx 直接发预压缩好的 .gz 文件,比实时压缩省得多。

问题二:为什么开了 sendfile,HTTPS 站点感觉没改善?因为 TLS 场景下数据要先加密,加密本身就要走用户态拷贝。这时候光靠 sendfile 不够,还得调 ssl_buffer_size(比如 16k)和 ssl_session_cache,让加密批次更大、来回切换更少。纯 HTTP 和 HTTPS 的 sendfile 收益差距,就在这儿。

问题三:明明开了 sendfile,CPU 还是高。那就得看别的方向了:是不是开了 gzip/brotli 实时压缩?是不是有慢客户端把连接挂着(ss -tn state established 看一下 send-q 堆积)?是不是同时跑了大量小文件请求小文件场景 sendfile 意义不大,反而应该配 open_file_cache 和 aio。别把所有"CPU 高"都算到 sendfile 头上。

五、改完怎么验证,别凭感觉

改配置、reload 之后,用同一台机器、同一个文件做前后对比。三个命令就够:

curl -o /dev/null -w "%{speed_download}\n" http://127.0.0.1/bigfile.iso 看单连接下载速度;pidstat -u 1 看 nginx 进程的 %usr 占用;ss -i | grep -c retrans 看有没有重传堆积。

我自己那台 2 核机器的实测结果:改之前单连接 18 MB/s,nginx worker 的 %usr 常年在 85% 以上;改之后单连接 41 MB/s,同等并发下 %usr 只有 30% 出头,用户端的下载进度条从"龟速"变成了"飞一般"。带宽从没用满过,问题从来就不在线路。

浏览器大文件下载进度与高速网络吞吐对比场景

六、顺手补一条:PHP 下载大文件的老办法

顺带提一个常被误诊的场景:如果下载接口是 PHP 写的,比如 readfile() 或 fpassthru(),那 Nginx 的 sendfile 帮不上忙数据是被 PHP 进程从磁盘读出来,再通过 FastCGI 一路传回 Nginx,等于绕了一大圈。正确做法是让 PHP 只回一个响应头,真正的文件发送交给 Nginx:

header('X-Accel-Redirect: /internal/file.zip');

然后在 Nginx 里配一个 internal 的 location。这样文件传输就完全走 sendfile 零拷贝了。这一招对"改成 nginx 直出后 CPU 反而更高"的工单特别好用很多人改完发现更慢,就因为 Nginx 那边开着 sendfile,PHP 那边还在傻乎乎地 echo 数据。

最后一个小清单

如果你也遇到"下载速度上不去、CPU 却很高",按这个顺序查:

一、strace -c 看有没有 sendfile 调用;二、没有就打开 sendfile on; 和 tcp_nopush on;;三、看是不是实时压缩在吃 CPU,是就上 gzip_static;四、是 PHP 在传文件就改 X-Accel-Redirect;五、最后才去怀疑带宽和线路。

配置改完记得 nginx -t 检查语法再 reload。sendfile 这一个开关,代码量是零,性能收益却是成倍的这类"免费的午餐"在服务器调优里其实不多,遇到问题的时候,值得多往配置文件上翻两页。

玩家留言 跟帖评论
查看更多评论
相关文章
猜你喜欢
同类下载