关于零拷贝。
首先我们得知道什么是用户空间与内核空间。
在常用的操作系统中为了保护系统中的核心资源,于是将系统设计为四个区域,越往里权限越大,所以Ring0被称之为内核空间,用来访问一些关键性的资源。Ring3被称之为用户空间。

用户态、内核态:线程处于内核空间称之为内核态,线程处于用户空间属于用户态。那么我们如果此时应用程序(应用程序是都属于用户态的)需要访问核心资源怎么办呢?那就需要调用内核中所暴露出的接口用以调用,称之为系统调用。例如此时我们应用程序需要访问磁盘上的文件。此时应用程序就会调用系统调用的接口 open方法,然后内核去访问磁盘中的文件,将文件内容返回给应用程序。大致的流程如下

然后我们了解一下非直接缓冲区与直接缓冲区。
既然我们要读取一个磁盘的文件,要废这么大的周折。有没有什么简单的方法能够使我们的应用直接操作磁盘文件,不需要内核进行中转呢?有,那就是建立直接缓冲区了。
非直接缓冲区:非直接缓冲区就是我们上面所讲内核态作为中间人,每次都需要内核在中间作为中转。

直接缓冲区:直接缓冲区不需要内核空间中转copy数据,而是直接在物理内存上直接申请空间,这块空间映射到内核地址和用户空间,而应用程序与磁盘之间的数据存储通过这块直接申请的物理内存进行交互。

关于直接缓冲区,当然NIO也做了优化,也可以内存映射文件。比申请物理内存要快一点。
public class TestIO5 {
public static void main(String[] args) {
String ZIP_FILE = "1023.zip";
File zipFile = new File(ZIP_FILE);
File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip");
long beginTime = System.currentTimeMillis();
try (
// jdk以后 这里声明的变量会自动关闭申请的资源
ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
WritableByteChannel writableByteChannel = Channels.newChannel(zipOut);
) {
MappedByteBuffer buffer = new RandomAccessFile(src,"r").getChannel().map(FileChannel.MapMode.READ_ONLY,0,src.length());
zipOut.putNextEntry(new ZipEntry("1023.zip"));
writableByteChannel.write(buffer);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("文件大小: " + src.length());
System.out.println("耗时: " + (System.currentTimeMillis() - beginTime));
}
}

关于零拷贝
sendfile优化,send file 是一个从磁盘到网卡驱动的 IO 优化。反过来,网卡到磁盘,是没有这个 IO 优化的。也就是说 transferFrom 方法并没有这种福利。Java send file api 是 transferTo 方法有的。
非零拷贝

DMA(direct memory copy)直接内存拷贝。不需要cpu。
零拷贝1:直接缓冲区拷贝。

零拷贝2:sendfile,linux2.1版本,提供了sendFile函数,其基本原理如下:数据不经过用户态,直接从内核缓冲区到网卡的socket buffer,同时,由于和用户态完全无关,就减少一次上下文切换。

其实零拷贝站在操作系统角度来看,是没有cpu拷贝,其实这里还是有一次cpu拷贝,

kernel buffer 到socket buffer。但是拷贝的信息很少,比如length,offset。消耗低 ,可以忽略

浙公网安备 33010602011771号