实际工作中:我们经常将磁盘上的文件读取到程序或内存中,然后以字节数组或流的方式发送传输出去
我们经常的操作是:将文件以流的方式去读取到buffer,这种操作操作系统(linux)的底层到底是怎么操作的
首先设计到io的操作,一定是调用的native方法
场景:我们从磁盘中读取数据,然后将数据读到内存中,将内存中的数据写到 网络的另一端
处理过程:
当我们程序发起读取文件的命令,我们的用户空间 会将系统调用命令 发送给内核空间,然后操作系统 由 用户空间切换为内核空间
内核 空间模式上真正的向磁盘发出请求,通过dma 将磁盘数据读入到内核空间的缓冲切(内核空间缓冲区用户不能直接使用,这个是第一次copy)
然后将数据从内核空间copy 到用户的缓冲区中,
然后我们需要将数据写到网路的另外一端
write需要文件数据从用户空间缓冲区 拷贝到内核缓冲区(这里的内核缓冲区和上面的不一样,是socket的换中去中),然后将数据写入到网络
然后操作完成
整个过程中存在四次上下文的切换(即不同空间之间的切花)和两次的不必要的数据copy
这个过程中,我们没有将数据处理,所以用户空间 只不过是个载体,这种就是传统的inputStream,outputStream
零拷贝完全取决于操作系统,系统提供这种机制,那就可以使用
nio的零copy可以提升性能:
内核空间收到sendfile的系统调用后,将磁盘上的数据,让后读到内核缓冲区,然后将数据复制socket缓冲区中(所以从操作系统角度上,实现了零copy,但是仍然存在copy过程)
当操作系统支持支持scatter和gather时,我们还可以继续优化,取消copy的过程(linux2.4版本以后)
一般来说硬件都有直接内存访问的机制,都期望地址是连续的,内核去请求数据,根据内核直接访问机制(或者用scatter和gather 或者操作系统提供了文件描述的机制,描述符描述了偏移量数据大小),这样socket 可以直接通过file的describer去探测到内核缓冲区是什么样的,就不需要去赋值
在上述过程中我们没有参与内核空间和磁盘 ,socket之间的过程,如果需要我们参与这个工程,我们需要磁盘映射文件
java中用MappedByteBuffer,修改内存就可以同步修改文件,文件的本身映射到内核空间,我们的应用程序可以访问内核文件,就好像文件在内
核空间一样