DMA (Direct Memory Access) 是直接内存访问技术,可能这个概念比较抽象,直接先来看看如果没有 DMA,进程一次完整的读磁盘操作的过程:
主要关注两个点:上下文切换和拷贝次数。在系统调用read()
的完整过程中:
一共经历了两次上下文切换,分别是从用户态转换为内核态和从内核态转换为用户态
CPU 一共进行了两次拷贝,分别是从磁盘控制缓冲区拷贝到 Page Cache 和从 Page Cache 拷贝到用户缓冲区
在这个过程中,可以看出 CPU 参与了整个数据的传输:磁盘控制缓冲区 -> Page Cache -> 用户缓冲区。有种杀鸡用牛刀的感觉,因为 CPU 资源是很宝贵的
基于这个问题就发明了 DMA 技术,DMA 可以主动将 I/O 设备中的数据拷贝到 Page Cache 中,而不需要 CPU 的参与,相当于解放了 CPU,在 DMA 拷贝的期间 CPU 可以去运行其它进程
同样的,主要关注两个点:上下文切换和拷贝次数。在系统调用read()
的完整过程中:
一共经历了两次上下文切换,分别是从用户态转换为内核态和从内核态转换为用户态
DMA 一共进行了一次拷贝,从磁盘控制缓冲区拷贝到 Page Cache
CPU 一共进行了一次拷贝,从 Page Cache 拷贝到用户缓冲区
注意:Page Cache 是内核缓冲区
在客户端/服务端架构中,假设服务端需要调用read()
读取磁盘中的文件数据,然后调用write()
向客户端发送该数据 (这两个函数都是系统调用)。基于上一部分介绍的 DMA 技术来看看这个过程:
同样的,主要关注两个点:上下文切换和拷贝次数。在整个过程中:
一共经历了四次上下文切换,分别为read()
和write()
系统调用时的上下文切换,每个系统调用两次
DMA 一共进行了两次拷贝,分别为从磁盘拷贝到 Page Cache 和从 socket 缓冲区拷贝到网卡
CPU 一共进行了两次拷贝,分别为从 Page Cache 拷贝到用户缓冲区和从用户缓冲区拷贝到 socket 缓冲区
从上面的过程可以看出开销还挺大,每次读数据➕写数据都要经历四次上下文切换和四次数据拷贝,而且其中有两次数据拷贝只是将内存中同一份数据拷贝到两个不同的地方而已 (两次 CPU 拷贝)
所以关于这个过程的优化也主要是从上下文切换和拷贝次数入手!!
mmap (Memory Map) 是内存映射,直接让用户空间和内核空间共享同一份数据,避免了用户空间和内核空间的数据来回拷贝
由于用户缓冲区和 Page Cache 共享了同一份缓存数据,所以就不需要「从 Page Cache 拷贝到用户缓冲区」和「从用户缓冲区拷贝到 socket 缓冲区」这两个过程。在整个过程中:
一共经历了四次上下文切换,分别为mmap()
和write()
系统调用时的上下文切换,每个系统调用两次
DMA 一共进行了两次拷贝,分别为从磁盘拷贝到 Page Cache 和从 socket 缓冲区拷贝到网卡
CPU 一共进行了一次拷贝,从 Page Cache 拷贝到 socket 缓冲区
相比于传统文件传输,减少了一次 CPU 拷贝的过程~
mmap 是从减少拷贝次数的角度优化,而本部分介绍的 sendfile 是从减少上下文切换的角度优化。在传统文件传输的过程中,使用read()
和write()
这两个系统调用;
read(file, tmp_buf, len);
write(socket, tmp_buf, len);
而sendfile()
系统调用将读写两个功能合二为一:
xxxxxxxxxx
// out_fd 表示写入的文件描述符;in_fd 表示读取的文件描述符
// offset 表示读取文件的偏移量;count 表示复制的长度
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
和 mmap 内存映射不同在于:调用sendfile()
时,数据对用户空间完全不可见,这是一次真正意义上的数据传输过程!
本部分要介绍的 sendfile + SG-DMA 有一个前提,必须要求网卡支持 SG-DMA (The Scatter-Gather Direct Memory Access) 技术,它进一步减少了从 Page Cache 拷贝到 socket 缓冲区过程,而直接可以从 Page Cache 拷贝到网卡 (DMA 拷贝)
这才算是真正意义上的零拷贝,也就是 CPU 完全不参与拷贝,全部交由 DMA 完成!!
方式 | CPU 拷贝 | DMA 拷贝 | 上下文切换 |
---|---|---|---|
传统方式 | 2 | 2 | 4 |
mmap | 1 | 2 | 4 |
sendfile | 1 | 2 | 2 |
sendfile + SC-DMA | 0 | 2 | 2 |
对于大文件传输,如果使用零拷贝技术,就会用 Page Cache,而 Page Cache 主要用来存放热点数据提高缓存命中率,所以如果 Page Cache 被大文件占据,将会大大降低命中率
大文件传输一般使用异步的方式,即不需要等到完全传输完成才返回,而是发起 I/O 请求后立刻返回,当 I/O 请求执行完后会通过回调函数通知 CPU
注意:异步传输不会使用 Cahce Page,而是直接从磁盘控制器缓冲区拷贝到用户缓冲区中