把本地文件发送到网络的三种方式对比

原文地址 https://blog.csdn.net/weixin_41385912/article/details/110212287

在“将本地磁盘中文件发送到网络中”这一场景中,零拷贝技术是提升 IO 效率的一个利器,为了对比出零拷贝技术的优越性,下面依次给出使用直接 IO 技术、内存映射文件技术、零拷贝技术实现将本地磁盘文件发送到网络中的过程。

1)直接 IO 技术

使用直接 IO 技术实现文件传输的过程如下图所示。

  

上图中,内核缓冲区是 Linux 系统的 Page Cahe。为了加快磁盘的 IO,Linux 系统会把磁盘上的数据以 Page 为单位缓存在操作系统的内存里,这里的 Page 是 Linux 系统定义的一个逻辑概念,一个 Page 一般为 4K。

可以看出,整个过程有四次数据拷贝,读进来两次,写回去又两次:磁盘-->内核缓冲区-->Socket 缓冲区-->网络。

直接 IO 过程使用的 Linux 系统 API 为:

  1.  ssize_t read(int filedes, void *buf, size_t nbytes);
  2.  ssize_t write(int filedes, void *buf, size_t nbytes);

2)内存映射文件技术

使用内存映射文件技术实现文件传输的过程如下图所示。

   

可以看出,整个过程有三次数据拷贝,不再经过应用程序内存,直接在内核空间中从内核缓冲区拷贝到 Socket 缓冲区。

内存映射文件过程使用的 Linux 系统 API 为:

  void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

3)零拷贝技术

使用零拷贝技术,连内核缓冲区到 Socket 缓冲区的拷贝也省略了,如下图所示:

  

内核缓冲区到 Socket 缓冲区之间并没有做数据的拷贝,只是一个地址的映射。底层的网卡驱动程序要读取数据并发送到网络上的时候,看似读取的是 Socket 的缓冲区中的数据,其实直接读的是内核缓冲区中的数据。

零拷贝中所谓的“零”指的是内存中数据拷贝的次数为 0。

零拷贝过程使用的 Linux 系统 API 为:

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

在 JDK 中,提供的:

FileChannel.transderTo(long position, long count, WritableByteChannel target);

方法实现了零拷贝过程,其中的第三个参数可以传入 SocketChannel 实例。例如客户端使用以上的零拷贝接口向服务器传输文件的代码为:

  

public static void main(String[] args) throws IOException {
    SocketChannel socketChannel = SocketChannel.open();
    socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
    String fileName = "test.zip";
 
    // 得到一个文件 channel
    FileChannel fileChannel = new FileInputStream(fileName).getChannel();
    
    // 使用零拷贝 IO 技术发送
    long transferSize = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
    System.out.println("file transfer done, size: " + transferSize);
    fileChannel.close();
}

 

  

posted on 2020-12-17 19:13  MaXianZhe  阅读(633)  评论(0)    收藏  举报

导航