RocketMq中零拷贝

2019-11-30  本文已影响0人  圣村的希望

    说起零拷贝之前,先来了解下服务器中文件数据通过网络传输到客户端的流程。作为应用服务器,其中会有很多从磁盘中读取数据,然后应用程序对加载到内存中的数据进行处理,然后通过网卡发送给客户端,传统数据处理通过以下两个函数实现:

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

    在这个过程中,数据流转的大致过程如下:

    可以见到,在这个过程中发生了2次cpu copy和2次DMA copy,以及发生了数次cpu状态切换。这个操作对于应用服务器来说很频繁,因此带来的开销也是非常大。
因此所谓的零拷贝就是,让其中的2次cpu拷贝省略掉,因为这两次cpu拷贝的数据其实已经在内存中,没有必要再让cpu参与进来进行数据的拷贝,浪费cpu。在大量文件读写的时候,这个优化带来的收益还是比较可观的。

零拷贝的实现方式有两种:

    mmap通过虚拟内存映射,让多个虚拟地址指向同一个物理内存地址,用户空间的虚拟地址和内核空间的虚拟地址指向同一个物理内存地址,这样用户空间和内核空间共享同一个内存数据。这样DMA引擎从磁盘上加载的数据不需要在内核空间和用户空间进行复制,减少了一次cpu拷贝。
    sendfile通过系统调用,并且规定了in_fd文件描述符必须是可以mmap的,sendfile只能将文件数据发送到socket中,sendfile减少了一次cpu状态的切换

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

    无论是mmap结合write方式还是sendfile方式都只是减少了一次cpu拷贝,而后DMA引擎还具有了收集功能,可以在内核缓存区发送到socket缓冲区的时候避免掉cpu复制,只是将缓冲区地址和数据长度发送给socket缓冲区,然后DMA引擎通过收集功能直接读取收集数据发送到网卡中。这里依赖DMA引擎的收集功能省略掉了最后一次cpu拷贝,到此才是真正的零拷贝。

    所谓的零拷贝就是避免数据在内核空间缓存区和用户空间缓缓冲区之间的复制,避免掉2次cpu复制,释放cpu。

    在RocketMq中采用的是mmap()结合write()方式来实现零拷贝。

FileChannel.map(MapMode.READ_WRITE, 0, fileSize);

    在java中还可以通过FileChannel.transferTo()来实现数据从文件描述符传输到socket中,它的底层是通过sendfile系统调用来实现。

上一篇下一篇

猜你喜欢

热点阅读