Java 两种zero-copy零拷贝技术mmap和sendfile的介绍

详细介绍了两种zero-copy零拷贝技术mmap和sendfile的概念和基本原理。

目录

1 标准IO

2 零拷贝

2.1 sendfile调用

2.1 mmap调用

2.2 MQ中的应用


1 标准IO

很多软件是基于server-client模式的,最常见的下载功能需要从Server端的磁盘中将文件通过网络发送到客户端中去。如果采用传统标准IO的方式(基于数据拷贝),那么需要如下步骤。

 传统标准IO通过网络传输数据,需要进行如下调用:

buffer = File.read 
Socket.send(buffer)

总共需要四步:

1. read(): 涉及到两次上下文切换以及两次数据拷贝;

        1.1 读取磁盘文件,将数据DMA Copy到操作系统内核缓冲区Page Cache;

        1.2 将内核缓冲区Page Cache的数据,CPU Copy到应用程序缓存;

2. send(): 涉及到两次上下文切换以及两次数据拷贝;

        2.1 将应用程序缓存中的数据,CPU Copy到socket网络发送缓冲区,即Socket Cache;

        2.2 将Socket Cache的数据, DMA Copy到网络,由网卡进行网络传输。
可以发现,完成一次读写操作,需要4次上下文切换,2次DMA数据拷贝,2次CPU数据拷贝,实际上,如果仅仅是数据传输,那么数据根本不会经过这么多次的拷贝。

DMA: Driect Memory Access, 它可以独立的直接读取系统内存,不需要CPU介入,像显卡,网卡之类都会用DMA。

2 零拷贝

零拷贝(Zero-copy)技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输数据时节省CPU周期和内存带宽。零拷贝技术可以减少数据拷贝和共享总线操作的次数,消除传输数据在存储器之间不必要的中间拷贝次数,从而有效的提高数据传输效率。而且,零拷贝技术减少了用户进程地址空间和内核地址空间因为上下文切换而带来的开销。

 常见的零拷贝技术分类:

1. 直接I/O:数据直接跨过内核缓冲区,在用户地址空间与I/O设备之间传递,内核只是进行必要的虚拟存储配置等辅助工作。

2. 数据传输不经过用户空间:当应用程序在数据传输过程中不需要对数据进行访问时,则可以避免将数据从内核空间到用户空间之间的拷贝,传输的数据在页缓存中就可以得到处理;Linux中提供类似的系统调用主要有mmap(),sendfile()以及splice().

3. 写时复制:数据不需要提前拷贝,而是当需要修改的时候再进行部分拷贝。COW是对数据在Linux的页缓存和用户进程的缓冲区之间的传输过程进行优化手段。

下面是介绍数据传输不经过用户空间的零拷贝技术:mmap和sendfilr,这也是Netty、Kafka、RocketMQ等矿界所使用的底层技术

2.1 sendfile调用

Linux 在版本 2.1 中引入了 sendfile() 这个系统调用,sendfile()是一种零拷贝的实现。Java对sendfile的支持就是NIO中的FileChannel.transferTo()或者transferFrom()

使用senfile进行网络数据传输流程为:

1. 发起sendfile() 系统调用,上下文切换一次。将文件中的数据DMA Copy到Page Cache中。

2. 继续将Page Cache中的数据CPU Copy到与Socket Cache中去。

3. 继续将Socket Cache的数据DMA Copy到网卡,由网卡进行网络传输。sendfile()系统调用返回,上下文切换一次。

可以看到整个流程,减少了一次CPU Copy,减少了两次的上下文切换,相比于传统IO确实提高了性能。

但是数据仍然需要一次从Page Cache到Socket Cache的CPU Copy,这个Copy能不能也去掉呢?

当然可以,Linux 2.4+ 版本之后,文件描述符结果被改变,借助DMA Gather(带有收集功能的DMA),sendfile()再次减少了一次 Copy 操作,变成了真正的零拷贝(没有CPU Copy)。

此时整个步骤变为:

1. 发起sendfile() 系统调用,上下文切换一次。将文件中的数据DMA Copy到Page Cache中。

2. 继续将Page Cache中的带有文件位置和长度信息的缓冲区描述符CPU Copy到Socket Cache中去,这部分拷贝很少的数据,可忽略。

3. 继续借助DMA Gather ,直接将Scocket Cache的真正数据DMA Copy到网卡,由网卡进行网络传输。这样就避免了最后一次CPU Copy。sendfile() 系统调用返回,上下文切换一次。

sendfile + DMA Gather流程如下:

sendfile + DMA Gather,使得整个传输只需要两次上下文切换,数据只需要两次DMA Copy,降低了上下文切换和数据拷贝带来的开销,极大的提升了数据传输的效率,没有CUP拷贝,是真正的零拷贝。

但是,sendfile调用有一个缺点,那就是无法在sendfile调用过程中修改数据,因此sendfile()只是适用于应用程序地址空间不需要对所访问数据进行处理的和修改情况,常见的就是文件传输,或者MQ消费消息的获取,如果想要在传输过程中修改数据,可以使用mmap系统调用。

mmap调用是一个比sendfile调用昂贵但优于传统I/O的零拷贝实现方式,而mmap调用则可以在中途直接修改Page Cache中的数据,这也是mmap零拷贝的优点。

2.1 mmap调用

mmap(Memory Mapped Files)是一种零拷贝技术,学名内存映射文件,Java的实现就是MappedByteBuffer,通过channel#map方法得到。

mmap将一个文件(或者文件的一部分)映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间的对应关系。注意这个时候并没有分配和映射到具体的物理内存空间,而是第一次加载这个文件的时候,通过MMU把之气那虚拟地址换算成物理地址,把文件加载进物理地址--内核空间的Page Cache中。

实现这样的映射关系后,进程就可以通过指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必要再调用read、write等系统调用函数,相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程之间的文件共享。

简单来说,使用mmap之后,数据无需拷贝到用户空间中,应用程序可以直接操作Page Cache中的数据。

mmap()代替read()调用之后的数据发送流程为:

buf = mmap(file, len);
write(sockfd, buf, len);

使用mmap技术之后,数据流转图如下所示:

 此时整个步骤变为:

1. mmap(): 涉及到两次上下文切换以及一次数据拷贝;

        1.1 发出mmap系统调用通知,发生一次上下文切换。通过DMA引擎将磁盘文件中的内容拷贝到内核空间的一个缓冲区(Page Cache)中。

        1.2 mmap系统调用返回,发生一次上下文切换。此后用户空间和内核空间共享整个缓冲区,用户空间就可以像在操作自己缓冲区中的数据一样操作这个由内核空间共享的缓冲区数据,而不需要将数据在内核空间和用户空间之间来回拷贝。

2. write():涉及到两次上下文切换以及两次数据拷贝;

        2.1 发起write调用,发生一次上下文切换。将缓冲区(Page Cache)的内容CPU Copy到Socket Cache

        2.2 将Socket Cache的数据,DMA Copy到网卡,由网卡进行网络传输。write调用返回,发生一次上下文切换。

这种mmap+write的方式相比于传统IO少了一次CPU Copy,从而极大地提高了效率。虽然性能弱于sendfile零拷贝,但其好处是可以在中途修改内存中的数据之后再传输。

另外,当应用程序往 mmap 输出数据时,此时就直接输出到了内核态的缓冲区数据,如果此时输出设备是磁盘的话,不会立即写磁盘,linux系统下通常会间隔是30秒由操作系统自动落盘,也可手动调用fsync()函数让其立即落盘,实现真正的持久化。

2.2 MQ中的应用

对于Kafka来说:

1. 数据从Producer到Broker,需要将来自网卡的消息持久化的磁盘中,Kafka中采用mmap的方式写,并且不会立即持久化到磁盘中,而是存入page cache内核缓冲区中就直接返回成功。后续有消费者来拉取消息的时候,也是先冲缓冲区中查找消息,如果有就直接发送给消费者,不会再查找磁盘,又提升了拉消息的性能。实际上它的日志文件并没有用到 mmap,而索引文件用了 mmap。
2. 数据从Broker到Consumer,需要将磁盘中的消息通过网卡发送出去,Kafka中采用sendfile的方式,将磁盘文件读到OS内核缓冲区后,直接转到socket buffer进行网络发送。

对于rocketMQ来说,如论是消息存储还是消费,都是采用mmap的方式,并且通过预热来减少大文件 mmap 因为缺页中断产生的性能问题。

参考资料:

Java 两种zero-copy零拷贝技术mmap和sendfile的介绍_刘Java的博客-CSDN博客_mmap+write对比sendfile

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值