传统IO
步骤:
- read调用会导致从用户模式更改为内核模式。同时,第一次拷贝开始,DMA(Direct Memory Access,即不使用CPU将数据拷贝到内存,而是DMA引擎将数据传输到内存)引擎从磁盘读取文件并将数据放入内核缓冲区。
- 第二次数据拷贝发生,即将内核缓冲区中的数据拷贝到用户缓冲区,同时发生从内核态到用户态的上下文切换。
- 当第三次数据拷贝发生时,我们调用write方法,系统将用户缓冲区的数据拷贝到Socket缓冲区中。这时,另一个上下文从用户模式切换到内核模式。
- 在第四次复制中,使用DMA引擎将数据从Socket缓冲区异步复制到网络协议引擎。在这种情况下,不需要上下文切换。
- write方法返回并再次从内核模式切换到用户模式。
mmap
mmap通过内存映射将文件映射到内核缓冲区。同时,用户可以和在内核空间共享数据。这样在网络传输过程中,可以减少内核空间到用户的拷贝数。
如上所示,用户缓冲区和内核缓冲区共享文件。如果要将文件传输到网络,则不再需要将其复制到用户空间,然后再把用户空间复制到Socket缓冲区。
内存复制次数:3
上下文切换次数:4
sendFile
Linux2.1:数据由DMA引擎从文件复制到内核缓冲区,然后write同时从内核缓冲区进入Socket Buffer。
内存复制次数:3
上下文切换次数:2
Linux2.4:避免从内核缓冲区复制到Socket缓冲区,直接复制到协议栈,从而减少了数据的再次复制。
内存复制次数:2
上下文切换次数:2
总结
mmap和sendFile的区别:
- mmap适合读写少量数据,sendFile适合传输大文件;
- mmap需要4次上下文切换和3次数据复制;sendFil2需要3次上下文切换和至少2次数据复制;
- sendFile可以使用DMA减少CPU拷贝,但是mmap不能。