(:3[▓▓▓▓▓▓▓▓]
零拷贝是指从操作系统的角度,没有CPU参与的拷贝。
DMA:
Direct Memory Access
直接内存拷贝(不使用CPU
)。
-
上下文切换需要
4
次,文件拷贝需要4
次 -
上下文切换与文件拷贝:
-
user
->kernal
切换到内核,将磁盘中的文件
DMA
拷贝 到内核缓冲区 -
kernal
->user
将内核缓冲区中的文件数据
CPU
拷贝 到用户空间 -
user
->kernal
将用户空间中的文件数据
CPU
拷贝 到socket
缓冲区将
socket
缓冲区中的文件数据DMA
拷贝到协议栈 -
kernal
- >user
-
-
mmap
通过内存映射,==将文件映射到内核缓冲区,同时用户空间可以共享内核缓冲区的数据==,这样在传输过程中就可以减少内核缓冲区到用户空间的拷贝 -
上下文切换需要
4
次,文件拷贝需要3
次-
user
->kernal
切换到内核,将磁盘中的文件
DMA
拷贝 到内核缓冲区 -
kernal
->user
==将内核缓冲区中的文件数据
mmap
映射 到用户空间(减少一次CPU拷贝
)== -
user
->kernal
将内核缓冲区中的文件数据
CPU
拷贝 到socket
缓冲区将
socket
缓冲区中的文件数据DMA
拷贝到协议栈 -
kernal
- >user
-
-
Linux 2.1 版本提供了
sendFile
函数,其基本原理:==数据不经过用户态,直接从内核缓冲区进入到socket
缓冲区==,同时和用户态完全无关,便减少一次(进、出,便是两次)上下文切换 -
上下文切换需要
2
次,文件拷贝需要3
次-
user
->kernal
切换到内核,将磁盘中的文件
DMA
拷贝 到内核缓冲区将内核缓冲区中的文件数据
CPU
拷贝 到socket
缓冲区(==不经过用户态==) -
kernal
->user
将
socket
缓冲区中的文件数据DMA
拷贝到协议栈
-
-
在Linux 2.4 版本时,对
sendFile
做了一些修改,==避免了从内核缓冲区到socket
缓冲区的CPU
拷贝,直接将文件数据从内核缓冲区拷贝到协议栈==,从而再减少了一次拷贝 -
上下文切换需要
2
次,文件拷贝需要2
次-
user
->kernal
切换到内核,将磁盘中的文件
DMA
拷贝 到内核缓冲区仅
CPU
拷贝 一些简单信息到socket
缓冲区(比如:length
、offset
)(==忽略该CPU
拷贝,不经过用户态==) -
kernal
->user
将
socket
缓冲区中的文件数据DMA
拷贝到协议栈
-
- 零拷贝,是指从操作系统的角度来看,
CPU
没有参与拷贝。在内核缓冲区没有重复文件数据,只有kernal
缓冲区存在一份文件数据 - 零拷贝不仅带来更少的数据复制,还能带来其他的性能优势:更少的上下文切换、更少的
CPU
缓存伪共享、以及无CPU
校验和计算 mmap
和sendFile
区别:mmap
适合小数据量读写,sendFile
适合大文件传输mmap
需要4
次上下文切换,3
次数据拷贝;sendFile
需要2
次上下文切换,最少2
次数据拷贝sendFile
可以利用DMA
方式,减少CPU
拷贝,mmap
则不能(必须从内核拷贝到socket
缓冲区)
JDK7
引入了AsynchronousI/O
,即AIO
。在进行I/O
编程中,常用到两种模式:Reactor
和Proactor
。Java
的NIO
就是Reactor
,当有事件触发时,服务器端得到通知,进行相应的处理AIO
即NIO2.0
,叫做异步不阻塞的IO
。AIO
引入异步通道的概念,采用了Proactor
模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用- 目前
AIO
还没有广泛应用,Netty
也是基于NIO
,而不是AIO
,因此我们就不详解AIO
了,有兴趣的同学可以参考《Java新一代网络编程模型AIO原理及Linux系统AIO介绍》
BIO | NIO | AIO | |
---|---|---|---|
IO模型 | 同步阻塞 | 同步非阻塞(多路复用) | 异步非阻塞 |
编程难度 | 简单 | 复杂 | 复杂 |
可靠性 | 差 | 好 | 好 |
吞吐量 | 低 | 高 | 高 |
举例说明
- 同步阻塞:到理发店理发,就一直等理发师,直到轮到自己理发
- 同步非阻塞:到理发店理发,发现前面有其它人理发,给理发师说下,先干其他事情,一会过来看是否轮到自己
- 异步非阻塞:给理发师打电话,让理发师上门服务,自己干其它事情,理发师自己来家给你理发