深入理解零拷贝
定义
全程由DMA进行数据搬运,CPU不再参与任何与数据搬运相关的事情,提高系统的吞吐量
DMA(Driect Memory Access)
-
负责IO设备和内存的数据交换
-
无DMA
-
有DMA
传统的文件读写
read(file,buf,len)
write(socket,buf,len)
- 流程图
注:每一次系统调用都会产生2次的状态切换 - 过程
- 磁盘数据拷贝到内核缓冲区,DMA完成
- 内核缓冲区拷贝到用户缓冲区,CPU完成
- 用户缓冲区拷贝到内核缓冲(socket缓冲区)区,CPU完成
- socket缓冲区拷贝到网卡缓冲区,DMA完成
整个过程进行了4次的状态切换和4次数据拷贝。
如何提高吞吐量?
- 减少状态切换,即减少系统调用
- 减少数据拷贝次数
mmap
- 原理
将内核缓存映射到用户缓存,内核与用户空间就不需要进行数据拷贝 - 代码
buf=mmap(file,len)
write(socketfd,buf,len)
-
流程
-
过程(4次状态切换,3次拷贝)
- 使用mmap调用,使得内核缓冲区和用户进程共享,两次状态切换
- 磁盘拷贝到内核缓冲区,使用DMA,一次拷贝
- 共享缓冲区write到socket缓冲区,使用CPU,二次拷贝,两次状态切换
- socket缓冲区到网卡缓冲区,使用DMA,三次拷贝
sendfile
- 函数
sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
-
原理
sendfile包含了目的端的fd和源端的fd,一个sendfile系统调用可以代替read和write系统调用,减少了两次系统调用。 -
2.1的内核
产生了2次状态切换和3次数据拷贝 -
2.4的内核
产生了两次状态切换和两次数据拷贝,且都是DMA拷贝 -
2.1和2.4的DMA之间的区别
- Scatter-gather DMA方式是与block DMA方式相对应的一种DMA方式。在DMA传输数据的过程中,要求源物理地址和目标物理地址必须是连续的。但是在某些计算机体系中,如IA架构,连续的存储器地址在物理上不一定是连续的,所以DMA传输要分成多次完成。如果在传输完一块物理上连续的数据后引起一次中断,然后再由主机进行下一块物理上连续的数据传输,那么这种方式就为block DMA方式。Scatter-gather DMA方式则不同,它使用一个链表描述物理上不连续的存储空间,然后把链表首地址告诉DMA master。DMA master在传输完一块物理连续的数据后,不用发起中断,而是根据链表来传输下一块物理上连续的数据,直到传输完毕后再发起一次中断。很显然,scatter-gather DMA方式比block DMA方式效率高。
- SG-DMA有三种工作方式,可以工作在Memory-to-Stream即存储接口到流接口,或者Stream-to-Memory即流接口到存储接口,以及Memory-to-Memory的存储器到存储器工作方式。工作在存储器到存储器的工作方式与普通DMA并无差别,没有数据流处理的优势。另外SG-DMA增加了Descriptor Processor,可以实现批量工作,从而进一步减轻Nios处理器的工作。只需要将Descriptor命令字写入到相应的Descriptor memory中
PageCache
- 缓存最近被访问的数据
- 预读功能
- 适合小数据量,大文件需要采用异步IO+直接IO
总结
要提高性能需要从两方面去优化,一个是用户态和内核态的切换;一个是数据拷贝。零拷贝的通过sendfile的方式在SG-DMA的加持下实现了CPU零拷贝,两次拷贝都是由DMA来完成的,但是sendfile用户是无法拿到数据buffer的,也就是数据不可在业务上进行更改,直接有磁盘到内核,再由内核至网卡缓冲区,中间用户只需要进行一次远程调用sendfile即可。
参考
- https://blog.youkuaiyun.com/rusbme/article/details/111877653