sendfile
是一个高效的文件传输系统调用,用于在文件描述符之间直接传输数据(通常是从文件到套接字),避免内核态与用户态之间的数据拷贝,从而显著提升性能。以下是关键细节和常见用法:
1. 函数原型(Linux)
#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
- 参数:
out_fd
:输出文件描述符(通常是套接字,如socket_fd
)。in_fd
:输入文件描述符(必须是支持内存映射的文件,如普通文件或块设备,不能是管道或字符设备)。offset
:指定从输入文件的哪个位置开始读取;如果为NULL
,则从当前文件偏移量开始。count
:要传输的字节数。
- 返回值:
- 成功时返回实际传输的字节数,失败返回
-1
并设置errno
。
- 成功时返回实际传输的字节数,失败返回
2. 核心优势
- 零拷贝(Zero-Copy):
数据直接从文件缓存(Page Cache)复制到网卡缓冲区,无需经过用户空间,减少 CPU 开销和内存带宽占用。 - 高性能:
对比传统read()
+write()
方式,性能提升显著(尤其在传输大文件时)。
3. 典型使用场景
示例:将本地文件发送到网络套接字
int file_fd = open("large_file.bin", O_RDONLY);
int socket_fd = /* TCP socket */;
off_t offset = 0;
struct stat file_stat;
fstat(file_fd, &file_stat);
// 发送整个文件
ssize_t sent_bytes = sendfile(socket_fd, file_fd, &offset, file_stat.st_size);
close(file_fd);
- 注意:
out_fd
(套接字)必须是非阻塞的。- Linux Kernel ≥2.6.33 后支持任意文件描述符作为目标(早期仅限套接字)。
4. 与其他技术的对比
方法 | 数据拷贝次数 | 适用场景 |
---|---|---|
read() +write() |
4次(内核↔用户态来回) | 小文件或需要处理数据 |
sendfile() |
2次(仅在内核内) | 大文件传输 |
splice() |
0次(管道缓冲优化) | Linux特有高阶场景 |
5. Windows/其他平台的替代方案
- Windows:
TransmitFile()
- macOS/FreeBSD: FreeBSD-derived systems also support
sendfile()
, but with slightly different semantics.
6. FAQ
- Q:
sendfile
能否加密传输?
A: No!需在应用层预先加密文件内容后再调用。 - Q: Nginx是否使用了此技术?
A: Yes!Nginx在静态文件分发时默认启用sendfile on;
。
若需进一步优化网络吞吐,可结合TCP_CORK或MSG_MORE控制发包节奏。
内容由零声教学AI助手提供,问题来源于学员提问