零拷贝技术|零拷贝技术-sendfile、mmap

零拷贝技术
文章参考:

知乎文章
小林图解系统
Linux高性能服务器编程
零拷贝技术大体来说就是没有通过CPU在内存层面进行拷贝数据,而是通过DMA进行传输
【零拷贝技术|零拷贝技术-sendfile、mmap】这样的提升是很大的,CPU就是应该让它用在关键的地方才行
前提:
Server将文件sendFile发送给客户端,那么传统的就是需要调用两个系统调用read & write
read(sendFile, tmp_buf, len); //从磁盘中读出来 write(socket , tmp_buf, len); //写到socket中去

传统模式下:
零拷贝技术|零拷贝技术-sendfile、mmap
文章图片

上下文切换&数据拷贝
我们使用了两个系统调用:readwrite, 发生了4次用户态内核态之间的切换
4次数据之间的拷贝:
  • 将磁盘的数据拷贝到操作系统内核的缓存区(DMA)
  • 将内核缓冲区中的数据拷贝到用户的缓冲区中(CPU)
  • 将拷贝到用户缓冲区的数据拷贝到内核的socket缓冲区中(CPU)
  • 将内核的socket缓冲区数据拷贝到网卡的缓冲区中(DMA)
sendfile实现零拷贝
#include ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); //目的端文件描述符、源文件描述符、源端偏移量、复制数据的长度 //返回值是实际复制的长度

sendfile函数在两个文件描述符之间传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,实现了零拷贝
??:sendfile几乎是专门为在网络上传输文件而设计的,因为源端文件必须指向真实的文件,目的端文件必须指向socket,因此是专为网络而生的
sendfile的模式
零拷贝技术|零拷贝技术-sendfile、mmap
文章图片

上下文切换&数据拷贝
总共发生了两次用户态内核态之间的转换,减少了两次上下文切换
数据之间的拷贝只有3次

代码实现起来也是更加的精简,假设我们Server参数:ip port filename
//...... //创建socket,bind,listen,accept //filefd:待发送的文件名,connfd:Server套接字filefd(filename, O_RDONLY); //打开文件 struct stat stat_buf; fstat(filefd, &stat_buf); //用于记录待发送文件状态//sendfile(connfd, filefd, NULL, stat_buf.st_size); //直接发送到套接字,及其精简 close(fd); //.....

mmap + write实现零拷贝
buf = mmap(file, len); write(sockfd, buf, len);

mmap()系统调用函数会将 内核缓冲区中的数据 映射到 用户空间 ,这样就不用实现用户空间的拷贝了

这样也是减少了一次拷贝,但是同样还是需要4次上下文切换
mmap传输大文件有优势,传输小文件可能出现碎片

    推荐阅读