零拷贝、sendfile

4 分钟阅读 578 字 + 490 词
参考答案
传输文件的时候是采用read和send来组合完成,这种当中的数据流向是怎么样的 呢?首先打开一个普通文件,数据会从磁盘通过DMA设备传输到内存,即文件对象当中的内核缓冲区部分,然后 调用read数据会从内核缓冲区拷贝到一个用户态的buf上面 (buf是read函数的参数),接下来 调用send,就将数据拷贝到了网络发送缓存区 ,最终实现了文件传输。
image-20240918114313022
image-20240921233433225
如何减少从内核文件缓冲区到用户态空间的拷贝呢?**解决方案就是使用mmap系统调用直接建立文件和用户态空间buf的映射。这样的话数据就减少了一次拷贝。**在非常多的场景下都会使用mmap来减少拷贝次数,典型的就是使用图形的应用去操作显卡设备的显存。
使用mmap系统调用 只能减少数据从磁盘文件的文件对象到用户态空间的拷贝 ,但是依然无法避免从用户态到内核已连接套接字的拷贝(因为网络设备文件对象不支持mmap)
image-20240920163129402
sendfile系统调用可以解决这个问题,它可以使数据直接在内核中传递而不需要经过用户态空间,调用sendfile系统调用可以直接将磁盘文件的文件对象的数据直接传递给已连接套接字文件对象,从而直接发送到网卡设备之上(在内核的底层实现中,实际上是让内核磁盘文件缓冲区和网络缓冲区对应同一片物理内存)
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
image-20240921233617352
使用sendfile的时候要特别注意, out_fd一般只能填写网络套接字的描述符,表示写入的文件描述符 ,in_fd一般是一个磁盘文件,表示读取的文件描述符。从上述的需求可以得知,sendfile只能用于发送文件方的零拷贝实现,无法用于接收方,并且发送文件的大小上限是2GB。