首先弄懂传统的I/O操作的过程:
首先,期间共发生了 4 次用户态与内核态的上下文切换,因为发生了两次系统调用,一次是 read()
,一次是 write()
,每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态。
上下文切换到成本并不小,一次切换需要耗时几十纳秒到几微秒,虽然时间看上去很短,但是在高并发的场景下,这类时间容易被累积和放大,从而影响系统的性能。
其次,还发生了 4 次数据拷贝,其中两次是 DMA 的拷贝,另外两次则是通过 CPU 拷贝的,下面说一下这个过程:
第一次拷贝
,把磁盘上的数据拷贝到操作系统内核的缓冲区里,这个拷贝的过程是通过 DMA 搬运的。第二次拷贝
,把内核缓冲区的数据拷贝到用户的缓冲区里,于是我们应用程序就可以使用这部分数据了,这个拷贝到过程是由 CPU 完成的。第三次拷贝
,把刚才拷贝到用户的缓冲区里的数据,再拷贝到内核的 socket 的缓冲区里,这个过程依然还是由 CPU 搬运的。第四次拷贝
,把内核的 socket 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程又是由 DMA 搬运的。
什么是DMA?
没有使用DMA之前,文件的IO操作,CPU需要发信号给磁盘控制器,让磁盘控制器准备数据。磁盘控制器准备好数据(把数据放入内核缓冲区)后,发中断信号给CPU,让CPU停下手头的工作进行IO。
然后把内存缓冲区的数据拷贝给用户缓冲区。这其中包含了四次上下文切换成本,因为切换之后还要切换回来所以是四次。
使用DMA之后,CPU不再参与数据搬运的工作,让DMA控制器去直接访问内存,把最占用时间的数据搬运的工作交给了DMA控制器来完成。
什么是虚拟内存?
了解零拷贝之前的重要知识就是虚拟内存技术。
各种内存策略都是在保证多进程在内存中的调用,如果要把所有的作业都放入内存才运行,那么会造成空间浪费和无法完全装下的问题。
所以利用局部性原理(时间局部性,空间局部性) : 存储单元被调用,临近的单元也很快会被调用。一个命令执行一次就可以会再次执行。
所以基于局部性原理,可以把用到的装入内存,暂时不用的留在外存。运行的时候用到再从外存调用进来,这样从用户来看好像空间确实大了很多。
利用以上特性,我们可以把内核和用户的虚拟地址映射到同一个物理地址,就可以减少切换次数了
mmap/write是什么?
内核缓冲区的数据拷贝到用户缓冲区是read()函数做的事情
内核缓冲区的数据直接映射到用户缓冲区是mmap()函数做的事情
说白了就是内核缓冲区和用户缓冲区进行共享,这样在write的时候都发生在内核,减少了一次拷贝的操作。拷贝还是要拷贝,只不过是不需要用户缓冲区这个中间人了。
系统调用还是两次,一个mmap一个write。切换还是四次。少了一次CPU copy但是还是需要wirte
- 应用进程调用了
mmap()
后,DMA 会把磁盘的数据拷贝到内核的缓冲区里。接着,应用进程跟操作系统内核「共享」这个缓冲区; - 应用进程再调用
write()
,操作系统直接将内核缓冲区的数据拷贝到 socket 缓冲区中,这一切都发生在内核态,由 CPU 来搬运数据; - 最后,把内核的 socket 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程是由 DMA 搬运的。
标签:DMA,乔亚,---,Day11,缓冲区,拷贝,拷贝到,CPU,内核 From: https://www.cnblogs.com/dwj-ngu/p/17120488.html