一、内核缓冲区Page Cache
进程在调用write函数写入文件描述符时,其实不是直接写入文件,而是
- 先中段到内核空间
- 然后再由内核空间写入内核缓冲区Page Cache(因为内核缓冲区相较于磁盘I/O速度更快)
- 最后内核会在适当的时机将内核缓冲区的内容写入磁盘
通过这种机制,可以减少操作磁盘I/O的次数,提高磁盘I/O效率(当然也可以通过mmap
函数将文件映射到内存中,这样就可以直接操作内存,内存中的操作既快速,又不用经过内核态<–>用户态的切换,更加高效)
如果想立即将内核缓冲区的内容写入磁盘,可以调用fsync
函数(调用fflush
函数也会将缓冲区的内容强制写入磁盘,而不等待缓冲区满再刷新)
二、零拷贝技术:mmap
磁盘相对于计算机中的CPU、内存来说,属于慢速设备,因此针对磁盘的优化,有我们上述讲到的通过内核态中的缓冲区Page Cache减少I/O访问次数,还有通过零拷贝技术来减少数据在内核态和用户态之间的拷贝次数
如果通过中断的方式,当我们发生系统调用read
时,内核会将磁盘中的数据拷贝到内核缓冲区(第一次拷贝),然后再将内核缓冲区的数据拷贝到用户缓冲区,这样就发生了两次拷贝
![](/2024/05/18/%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6/two_copy.png)
也就是说在大量数据拷贝过程都需要CPU参与搬运,这样会浪费CPU资源,降低效率
因此进一步可以通过DMA直接内存访问技术进行优化:
在不占用CPU资源的情况下(此时CPU可以执行其它任务),将数据从磁盘拷贝到内核缓冲区
当DMA读取了足够多的数据,再通知CPU将数据从内核缓冲区拷贝到用户缓冲区,这样就只发生了一次拷贝
![](/2024/05/18/%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6/one_copy.png)
即使采用DMA技术,还是需要通过CPU进行一次拷贝,具体通过read
和write
的流程如下所示(各发生2次用户态<–>内核态的上下文切换,一次切换需要耗时几十纳秒到几微秒;以及各发生一次DMA拷贝+一次CPU拷贝):
![](/2024/05/18/%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6/read_write.png)
而实际上,多次拷贝是没必要的,我们可以通过mmap
函数将文件映射到内存中,使read
和write
直接操作共享的内核缓冲区,这样就可以减少一次拷贝,具体流程如下:
![](/2024/05/18/%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6/mmap.png)
参考:什么是零拷贝?
三、I/O多路复用技术
在文章WebServer学习3:socket编程与epoll实现I/O复用中已经有详细介绍,这里不再赘述
四、事件驱动模型
在文章WebServer学习4:并发事件驱动模式Reactor和Proactor中已经有详细介绍,这里不再赘述