什么是DMA与零拷贝
一、IO
1. 传统IO
- CPU 发出对应的指令给磁盘控制器,然后返回;
- 磁盘控制器收到指令后,于是就开始准备数据,会把数据放入到磁盘控制器的内部缓冲区中,然后产生一个中断;
- CPU 收到中断信号后,停下手头的工作,接着把磁盘控制器的缓冲区的数据一次一个字节地读进自己的寄存器,然后再把寄存器里的数据写入到内存,而在数据传输的期间 CPU 是无法执行其他任务的。
2. DMA技术
将IO设备与内存之间的数据传输工作交给DMA控制器,这样 CPU 就可以去处理别的事务。
二、文件传输
1. 传统文件传输
四次用户态/内核态切换 + 四次数据拷贝
2. mmap + write
用 mmap() 替换 read() 系统调用函数。mmap() 系统调用函数会直接把内核缓冲区里的数据「映射」到用户空间,这样就不需要再从内核拷贝数据到用户缓冲区。四次用户态/内核态切换 + 三次数据拷贝
3. sendfile
sendfile接收源端和目的端的文件描述符作为参数,通过一次sendfile系统调用就可以直接在内核态中将内核缓冲区里的数据拷贝到 socket 缓冲区。两次用户态/内核态切换 + 三次数据拷贝
4. 支持SG-DMA的sendfile(零拷贝)
如果网卡支持 SG-DMA 技术,那么在sendfile() 系统调用的过程中网卡的 SG-DMA 控制器可以直接将数据从PageCache拷贝到网卡,不需要CPU在内存中进行数据拷贝,因此称为零拷贝。两次用户态/内核态切换 + 两次数据拷贝
5. 大文件传输 (异步 + 直接IO)
大文件传输如果采用零拷贝,由于文件太大,可能某些部分的文件数据被再次访问的概率比较低,这样就会带来 2 个问题:
- PageCache 由于长时间被大文件占据,其他「热点」的小文件可能就无法充分使用到 PageCache,于是这样磁盘读写的性能就会下降了;
- PageCache 中的大文件数据,由于没有享受到缓存带来的好处,但却耗费 DMA 多拷贝到 PageCache 一次;
因此在高并发的场景下,针对大文件的传输的方式,应该使用「异步 I/O + 直接 I/O」来替代零拷贝技术。绕开 PageCache 的 I/O 叫直接 I/O,使用 PageCache 的 I/O 则叫缓存 I/O。通常,对于磁盘,异步 I/O 只支持直接 I/O。它把读操作分为两部分:
- 前半部分,内核向磁盘发起读请求,但是可以不等待数据就位就可以返回,于是进程此时可以处理其他任务;
- 后半部分,当内核直接将磁盘中的数据拷贝到进程缓冲区后,进程将接收到内核的通知,再去处理数据;
在 Nginx 里,可以通过配置,设定一个文件大小阈值,针对大文件使用异步 IO 和直接 IO,而对小文件使用零拷贝。
参考:DMA与零拷贝
三、网络数据传输流程