陆小曼|Java IO,讲的实在是太好了,这篇( 九 )


操作系统最重要的就是内核 , 它既可以访问受保护的内存 , 也可以访问底层硬件设备 , 所以为了保护内核的安全 , 操作系统将底层的虚拟空间分为了用户空间和内核空间 , 其中用户空间就是给用户进程使用的 , 内核空间就是专门给操作系统底层去使用的 。
这张照片就会从磁盘中读出到内核缓冲区中保存 , 然后操作系统将内核缓冲区中的这张图片字节数据拷贝到用户进程的缓冲区中保存下来 , 对应着下面这幅图
导致IO性能瓶颈的原因:内核空间与用户空间之间数据过多无意义的拷贝 , 以及多次上下文切换
操作状态用户进程请求读取数据用户态->内核态操作系统内核返回数据给用户进程内核态->用户态用户进程请求写数据到硬盘用户态->内核态操作系统返回操作结果给用户进程内核态->用户态
在用户空间与内核空间之间的操作 , 会涉及到上下文的切换 , 这里需要CPU的干预 , 而数据在两个空间之间来回拷贝 , 也需要CPU的干预 , 这无疑会增大CPU的压力 , NIO是如何减轻CPU的压力?运用操作系统的零拷贝技术 。
操作系统的零拷贝所以 , 操作系统出现了一个全新的概念 , 解决了IO瓶颈:零拷贝 。 零拷贝指的是内核空间与用户空间之间的零次拷贝 。
零拷贝可以说是IO的一大救星 , 操作系统底层有许多种零拷贝机制 , 我这里仅针对JavaNIO中使用到的其中一种零拷贝机制展开讲解 。
在JavaNIO中 , 零拷贝是通过用户空间和内核空间的缓冲区共享一块物理内存实现的 , 也就是说上面的图可以演变成这个样子 。
现在我们重新来拷贝文件 , 就会变成下面这个步骤:
用户进程通过系统调用read()请求读取文件到用户空间缓冲区(第一次上下文切换) , 用户态->核心态 , 数据从硬盘读取到内核空间缓冲区中(第一次数据拷贝)系统调用返回到用户进程(第二次上下文切换) , 此时用户空间与内核空间共享这一块内存(缓冲区) , 所以不需要从内核缓冲区拷贝到用户缓冲区用户进程发出write()系统调用请求写数据到硬盘上(第三次上下文切换) , 此时需要将内核空间缓冲区中的数据拷贝到内核的Socket缓冲区中(第二次数据拷贝)由DMA将Socket缓冲区的内容写到硬盘上(第三次数据拷贝) , write()系统调用返回(第四次上下文切换)整个过程就如下面这幅图所示 。


推荐阅读