当前位置: 首页 > news >正文

sendfile函数与传统 read+write 拷贝相比的优势

sendfile函数

sendfile 是 Linux 系统提供的 “零拷贝(Zero-Copy)核心系统调用”,核心功能是”在两个文件描述符之间直接传输数据“,全程在内核态完成,避免用户态与内核态的频繁数据拷贝,性能远优于 read+write 组合。下面从【核心特性、函数原型、参数详解、工作原理、使用场景、限制与注意事项】等方面全面讲解。

一、核心特性(为什么用 sendfile?)

sendfile 的核心优势是 “零拷贝”,对比传统 read+write 拷贝流程,差异非常明显:

1. 传统 read+write 拷贝流程(4次拷贝+2次系统调用)

磁盘 → 内核缓冲区(read)→ 用户缓冲区 → 内核缓冲区(write)→ 目标文件/网络

  • 2次用户态↔内核态切换(readwrite 各1次);

  • 4次数据拷贝(磁盘→内核、内核→用户、用户→内核、内核→目标);

  • 效率低,大文件/高并发场景下性能瓶颈明显。

2. sendfile 零拷贝流程(2次拷贝+1次系统调用)

磁盘 → 内核缓冲区 → 目标文件/网络(全程内核态)

  • 1次系统调用(仅 sendfile),无用户态↔内核态切换;

  • 2次数据拷贝(均在内核态:磁盘→内核缓冲区、内核缓冲区→目标);

  • 跳过用户缓冲区,减少数据拷贝开销,大文件传输性能提升显著。

二、函数原型与头文件

返回值说明

#include <sys/sendfile.h>ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
  • 成功:返回实际传输的字节数(ssize_t 类型,支持大文件);

  • 失败:返回 -1,并设置 errno(需通过 perror 查看具体错误)。

三、参数详解(重点!)

sendfile 的参数有严格限制,需明确每个参数的含义和合法范围:

参数名

类型

含义与要求

out_fd

int

输出文件描述符(数据写入的目标)

✅ 合法类型:文件、网络套接字(socket

❌ 不支持:管道(pipe)、终端(tty)等非“可发送”描述符

in_fd

int

输入文件描述符(数据读取的来源)

✅ 合法类型:必须是"支持 mmap 的文件描述符"(普通文件、块设备文件等)

❌ 不支持:网络套接字、管道、终端等

offset

off_t*

输入文件的偏移量指针(控制从哪个位置开始读取)

NULL:使用输入文件当前的偏移量,传输后自动更新偏移量

✅ 非 NULL:从指定偏移量读取,传输后不更新文件本身的偏移量(需手动处理)

count

size_t

计划传输的字节数(不能超过文件实际大小,否则只传输到文件末尾)

四、关键参数限制(避坑重点!)

  1. in_fd 必须是 “可内存映射的文件”:

    1. 支持:普通文件(如文本、二进制文件)、块设备文件;

    2. 不支持:网络套接字(socket)、管道(pipe)、FIFO、终端(tty)、字符设备文件(如 /dev/zero)。

    3. 原因:sendfile 依赖内核的“零拷贝机制”,需要将输入文件的数据映射到内核缓冲区,非文件类型的描述符无法支持该操作。

  2. out_fd 支持的类型:

    1. 主要支持:文件(普通文件、块设备)、网络套接字(SOCK_STREAM 类型,如 TCP 套接字);

    2. 不支持:管道、FIFO、终端。

    3. 典型场景:Web 服务器向客户端发送静态文件(in_fd=文件描述符out_fd=TCP 套接字)、本地文件拷贝(in_fd=源文件out_fd=目标文件)。

五、工作原理(简化版)

  1. 调用 sendfile 后,内核直接从 in_fd 对应的文件中读取数据到内核缓冲区(磁盘→内核,1次拷贝);

  2. 内核无需将数据拷贝到用户缓冲区,直接将内核缓冲区的数据写入 out_fd 对应的目标(内核→目标文件/网络,1次拷贝);

  3. 全程无用户态参与,仅1次系统调用,大幅减少 CPU 开销和内存带宽占用。

六、典型使用场景

1. 本地文件高效拷贝(前序示例场景)

  • in_fd:源文件(只读打开,O_RDONLY);

  • out_fd:目标文件(只写+创建+截断,O_WRONLY | O_CREAT | O_TRUNC);

  • 优势:比 cp 命令(底层可能用 read+write)或自定义 read+write 拷贝快数倍(大文件场景)。

2. 网络服务发送大文件(最核心场景)

比如 Web 服务器(Nginx、Apache)向客户端发送静态资源(图片、视频、压缩包):

  • in_fd:静态文件(如 /var/www/test.mp4);

  • out_fd:TCP 套接字(与客户端建立的连接);

  • 优势:避免用户态拷贝,支持高并发、大文件传输,是高性能 Web 服务器的核心优化手段。

示例代码(网络发送文件):

#include <sys/sendfile.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h>int main() {// 1. 创建 TCP 套接字并绑定、监听(简化版,实际需处理连接)int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(8080), .sin_addr.s_addr = INADDR_ANY};bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));listen(sockfd, 5);int client_fd = accept(sockfd, NULL, NULL); // 接收客户端连接// 2. 打开要发送的文件int file_fd = open("large_file.mp4", O_RDONLY);struct stat st;fstat(file_fd, &st); // 获取文件大小// 3. 用 sendfile 向客户端发送文件(零拷贝)ssize_t sent = sendfile(client_fd, file_fd, NULL, st.st_size);if (sent == -1) perror("sendfile error");else printf("发送 %ld 字节到客户端\n", sent);// 4. 关闭资源close(file_fd);close(client_fd);close(sockfd);return 0;
}

七、常见错误与 errno 解析

errno

含义

排查方向

EBADF

文件描述符无效(in_fd/out_fd

检查 open/socket 返回值是否为 -1,是否重复关闭

EINVAL

参数不合法

1. in_fd 不是可映射文件(如网络套接字);2. out_fd 不支持发送(如管道);3. offset 指针无效

ENOSPC

目标设备空间不足

检查目标文件所在磁盘的剩余空间

EIO

底层 I/O 错误(如磁盘读取失败)

检查源文件是否存在、是否有读权限

八、限制与注意事项

  1. 平台兼容性sendfile 是 Linux 特有系统调用,不支持 Windows、macOS(macOS 有类似的 sendfile 但参数不同,BSD 系有 sendfile64),跨平台程序需慎用。

  2. 大文件支持sendfilecountsize_t 类型(32位系统最大 4GB,64位系统无限制),若需在32位系统传输超过4GB的文件,需使用 sendfile64(头文件相同,参数一致,仅支持大文件)。

  3. 偏移量处理:若 offsetNULLsendfile 不会更新 in_fd 的文件偏移量,需手动通过 *offset += 实际传输字节数 维护偏移量(适用于多线程并发读取同一文件的场景)。

  4. 不能用于双向传输sendfile 是单向传输(in_fdout_fd),若需双向传输(如客户端↔服务器互传文件),需分别调用 sendfile

  5. 不支持非阻塞 I/Osendfile 本身是阻塞调用,但如果 out_fd 被设置为非阻塞(如 fcntl(out_fd, F_SETFL, O_NONBLOCK)),则 sendfile 会返回实际传输的字节数(可能小于 count),或返回 -1 并设置 errno=EAGAIN(需重试)。

九、总结

sendfile 的核心价值是 “零拷贝高效传输”,核心适用场景是【本地大文件拷贝】和【网络大文件发送】。它不是进程间通信工具(IPC),而是聚焦于“文件描述符间数据传输”的高性能系统调用。

使用时需重点关注:

  • in_fd 必须是可映射文件(普通文件);

  • out_fd 支持文件或 TCP 套接字;

  • 结合 fstat 获取文件大小,避免传输不完整;

  • 处理 errno 错误,确保程序健壮性。

http://www.dtcms.com/a/597326.html

相关文章:

  • ARL部署
  • 突破智能体训练瓶颈:DreamGym如何通过经验合成实现可扩展的强化学习?
  • 如何学习销售技巧,提高销售能力?
  • 建设北京公司网站兰州网站建设方案
  • 乐趣做网站公众信息服务平台
  • 有源代码怎么制作网站企业网络营销推广方案策划
  • C#使用Chart图表控件实时显示运动坐标
  • 数据结构---哈夫曼树的实现
  • 扁平 网站 模板物联网网站开发公司
  • 新增网站建设方案六安网站建设六安
  • DeepSeek-OCR——上下文视觉压缩:同等长度下,通过更少的视觉token解决长上下文处理难题
  • 从同步耦合到异步解耦:消息中间件如何重塑系统间的通信范式?
  • AI: n8n工作流自动化
  • 上市公司数字化转型策略数据(2000-2024)
  • RBAC权限控制
  • bat 脚本100分钟后自动关机
  • STM32 + MQTT 实现物联网设备数据上报与远程控制(实战教程)
  • 新开神途手游发布网站怎样建网站买东西
  • 网站开发强制开启浏览器极速模式网站建设 总体目标
  • 苏州网站设计公司有哪些成全免费观看在线看
  • 裴东莞嘘网站汉建设专门做问卷的网站
  • 生产效率提升利器!桌面五轴加工设备赋能定制工具制造
  • grafana 通过 provider 导入的 dashboard 报错
  • 网站开发 财务自由西安企业网站设计制作
  • coze开发基础
  • 革新音频编辑:基于LLM的大间隔学习实现高表现力控制与零样本TTS
  • 可以在手机建网站的东莞高端商城网站制作
  • Node.js 开发环境搭建全攻略(2025版)
  • colinmollenhour/credis 1.17 bug
  • 企业级SQL审核优化工具 PawSQL(4) — 生态集成