O函数之sendfile函数[通俗易懂]

O函数之sendfile函数[通俗易懂]sendfile 函数在两个文件描述符之间传递数据 完全在内核中操作 从而避免了内核缓冲区和用户缓冲区之间的数据拷贝 效率很高 被称为零拷贝 函数定义为 include ssize t senfile int out fd int in fd off t offset size t count in fd 参数是待读出内容的文件描述符 out fd 参数是待写入内容的文件描述符

sendfile函数在两个文件描述符之间传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,被称为零拷贝。函数定义为:

#include
ssize_t senfile(int out_fd,int in_fd,off_t* offset,size_t count);

in_fd参数是待读出内容的文件描述符,out_fd参数是待写入内容的文件描述符。offset参数指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流默认的起始位置。count参数指定文件描述符in_fd和out_fd之间传输的字节数。

in_fd必须是一个支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket和管道,而out_fd必须是一个socket

首先我们来看看传统的read/write方式进行socket的传输。
当需要对一个文件进行传输的时候,具体流程细节如下:

1:调用read函数,文件数据copy到内核缓冲区
2:read函数返回,文件数据从内核缓冲区copy到用户缓冲区
3:write函数调用,将文件数据从用户缓冲区copy到内核与socket相关的缓冲区
4:数据从socket缓冲区copy到相关协议引擎。

在这个过程中发生了四次copy操作。

硬盘->内核->用户->socket缓冲区(内核)->协议引擎。

而sendfile的工作原理呢??

1、系统调用 sendfile() 通过 DMA 把硬盘数据拷贝到 kernel buffer,然后数据被 kernel 直接拷贝到另外一个与 socket 相关的 kernel buffer。这里没有 用户态和核心态 之间的切换,在内核中直接完成了从一个 buffer 到另一个 buffer 的拷贝。
2、DMA 把数据从 kernel buffer 直接拷贝给协议栈,没有切换,也不需要数据从用户态和核心态,因为数据就在 kernel 里。

测试代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int main(int argc,char *argv[])
{
if(argc<=3)
{
printf("usage:%s ip_address port_number filename\n",basename(argv[0]));
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
const char* file_name = argv[3];

int filefd = open(file_name,O_RDONLY);
assert(filefd>0);

struct stat stat_buf;
fstat(filefd,&stat_buf);

struct sockaddr_in address;

bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port = htons(port);

int sock = socket(PF_INET,SOCK_STREAM,0);
assert(sock>=0);

int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));
assert(ret!=-1);

ret = listen(sock,5);
assert(ret!=-1);

struct sockaddr_in client;
socklen_t client_addrlength = sizeof(client);

int connfd = accept(sock,(struct sockaddr*)&client,&client_addrlength);

if(connfd<0)
{
printf("errno is %d\n",errno);
}
else
{
sendfile(connfd,filefd,NULL,stat_buf.st_size);
close(connfd);
}
close(sock);
return 0;
}

然后进行

在另外一个虚拟机上telnet

编程小号
上一篇 2025-01-29 14:27
下一篇 2025-03-27 19:40

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/hz/121833.html