NIO零拷贝
普通IO操作流程
- 用户程序向内核程序发起读请求
- cpu从用户模式切换到内核模式,内核模式向磁盘发出读取数据的请求
- 磁盘将数据读取到内核空间的缓冲区
- 内核将数据从缓冲区拷贝到用户空间的缓冲区
- 执行逻辑代码
- 执行write方法,将数据写到网络的另一端。
- 将数据从用户空间拷贝回内核空间
- 将数据真正的写到磁盘或者socket中
这种方式会导致系统遇到瓶颈,为传统IO操作
零拷贝
零拷贝依赖于操作系统,操作系统实现了,则实现,没有实现则没有实现,跟java程序无关。
- 从图中可以看出对用户空间的数据拷贝已经没了。
- 内核空间收到sendfile()申请
- 内核空间向磁盘发送数据读取请求
- 磁盘将数据读取到内核空间缓冲区
- 将数据写入到socket缓冲区中
- 通过socket缓冲区向网络客户端发送数据
- 结果返回
- sendfile()调用返回
这种操作我们称为零拷贝,相对与上一种情况有了极大的提升。
思考:
我们能不能减少磁盘拷贝到内核的情况,而是将磁盘数据直接拷贝到socket缓冲区中??
第二版零拷贝
这种方式实现了从磁盘空间直接读入Socket缓冲区
最终版零拷贝
- 用户程序发送sendfile()指令给内核空间并返回到用户空间
- 内核空间从磁盘采用DMA copy将数据从磁盘拷贝到了内核空间
- 将内核缓冲区的数据的文件描述符的信息拷贝到socketbuffer,而不是数据。文件描述中含有数据的内存地址等信息。
- protocol协议直接通过socketbuffer中读取到文件描述符信息从而找到kernel buffer存储数据的内存地址以及所需读取字符的长度。并将kernel buffer中的数据发送服务端
内存映射:
问题:上面的情况用户无法参与到数据的读取与写入的过程,如果用户需要参与呢??
内存映射文件:
通过代码的方式将文件映射到系统内核空间,这样修改文件只需要访问内核空间就可以。