对sendfile的理解
2026/6/10 1:18:57 网站建设 项目流程

一、先搞懂:传统文件传输为什么慢?

先看我们平时写的普通文件传输代码(socket 发送文件):

// 伪代码:传统 read + write 传输

char buf[4096];

read(fd, buf, 4096); // 1. 内核→用户态:拷贝文件到缓冲区

write(sock, buf, 4096);// 2. 用户态→内核:拷贝缓冲区到socket

传统传输的 4 次拷贝 + 2 次上下文切换

  1. 硬盘 → 内核缓冲区(DMA拷贝)

  2. 内核缓冲区 → 用户缓冲区(CPU拷贝)

  3. 用户缓冲区 → Socket内核缓冲区(CPU拷贝)

  4. Socket内核缓冲区 → 网卡(DMA拷贝)

问题:CPU 干了两次无用功,上下文切换频繁,大文件传输极慢。

二、sendfile 核心原理:零拷贝

sendfile 直接让内核在内核态完成文件到 socket 的传输,完全不经过用户态!

sendfile 只有 2 次拷贝 + 0 次用户态切换

  1. 硬盘 → 内核文件缓冲区(DMA)

  2. 内核文件缓冲区 → Socket缓冲区(DMA/硬件优化)

  3. 直接发给网卡

一句话总结:

sendfile 让内核直接把文件数据扔给网卡,用户程序不参与数据拷贝,实现零拷贝。

三、sendfile 函数原型

这是你写代码必须记住的标准格式:

#include <sys/sendfile.h>

ssize_t sendfile(

int out_fd, // 输出文件描述符:必须是 socket!

int in_fd, // 输入文件描述符:必须是普通文件/磁盘文件!

off_t *offset, // 文件读取起始偏移量(传 NULL 表示从当前位置读)

size_t count // 要传输的字节数

);

返回值(必须会)

  • 成功:返回实际传输的字节数

  • 失败:返回 -1,设置 errno

四、sendfile 硬性限制

sendfile 不是万能的,有严格限制,必须记死:

1.out_fd 必须是 socket 描述符
  • 不能是普通文件、管道、终端

  • 只能用于网络发送文件

2.in_fd 必须是普通磁盘文件

  • 不能是 socket、管道、内存文件

  • 必须支持 seek 寻址(普通文件都支持)

3.无法修改数据

  • 内核直接转发,你不能在传输时加密、压缩、修改内容

  • 纯转发场景才用它

4.Linux 专属

  • Windows、macOS 有类似函数,但不通用

  • 跨平台程序不能只靠 sendfile

五、最简可运行代码

这是最标准的 sendfile 服务端代码:

#include <stdio.h>

#include <sys/sendfile.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/socket.h>

int main() {

// 1. 打开要发送的文件

int file_fd = open("test.txt", O_RDONLY);

struct stat st;

fstat(file_fd, &st); // 获取文件大小

// 2. 假设已经建立好 socket 连接(conn_fd 是客户端socket)

int conn_fd = accept(...); // 省略socket创建、绑定、监听

// 3. sendfile 零拷贝传输

off_t offset = 0; ssize_t ret = sendfile(conn_fd, file_fd, &offset, st.st_size);

if (ret < 0) {

perror("sendfile failed");

} else {

printf("成功传输 %zd 字节\n", ret);

}

close(file_fd);

close(conn_fd);

return 0;

}

六、必须会的核心知识点

1. 什么是零拷贝?

零拷贝 = 避免 CPU 在用户态和内核态之间拷贝数据 sendfile 是 Linux 最经典的零拷贝实现。

2. sendfile 优势

  • 速度极快(大文件提升 30%~200%)

  • 减少 CPU 占用

  • 代码极简

  • 内核级优化,稳定可靠

3. 适用场景

  • Web 服务器发送静态文件(html、css、js、图片)

  • 文件服务器下载

  • 纯转发、不修改数据的网络传输

4. 不适用场景

  • 需要加密/压缩文件

  • 跨平台程序

  • 传输非磁盘文件(socket→socket 不行)

5. 与 mmap 的区别(必考题)

  • sendfile:简单、专用、零拷贝、不能改数据

  • mmap:映射文件到内存、可以修改数据、复杂

七、一句话总结(最核心)

sendfile = 内核态零拷贝 + 文件直接发socket + 不经过用户态 + 高性能 + 只能转发磁盘文件

总结

  1. 原理:内核直接转发文件到 socket,零拷贝、无用户态切换

  2. 函数:sendfile(out_socket, in_file, offset, size)

  3. 限制:输出必须是 socket,输入必须是普通文件

  4. 用途:Web服务器、文件下载、高性能网络转发

  5. 优势:比 read/write 快得多,CPU 占用低

参考链接 :0voice · GitHub

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

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

立即咨询