linux零拷贝原理,RocketMQ&Kafka使用对比

RocketMQ Kafka Consumer消费消息过程,使用了零拷贝,零拷贝包含以下两种方式

1. 使用 mmap + write 方式

RocketMQ 选择了这种方式,mmap+write 方式,因为有小块数据传输的需求,效果会比 sendfile 更好。但是RocketMQ控制mmap映射的内存分配与释放的地方非常复杂,稍有不慎就会出问题。

优点:即使频繁调用,使用小块文件传输,效率也很高。

缺点:不能很好的利用 DMA 方式,会比 sendfile 多消耗 CPU,内存安全性控制复杂,需要避免 JVM Crash问题。

2. 使用 sendfile 方式

优点:可以利用 DMA 方式,消耗 CPU 较少,大块文件传输效率高,无内存安全性问题。

缺点:小块文件效率低于 mmap 方式,只能是 BIO 方式传输,不能使用 NIO。

sendfile:FileChannel.transferTo()只有源为FileChannel才支持transfer这种高效的复制方式,其他如SocketChannel都不支持transfer模式。当然,目的Channel没有这种限制。所以一般可以做FileChannel->FileChannel和FileChannel->SocketChannel的transfer。

KAFKA的索引文件使用mmap+write 方式,data文件使用sendfile 。

下面这个问题就是RocketMQ使用mmap后的潜在问题:

下面是详细介绍这两种零拷贝方式:

mmap 文件映射

通常情况下,我们可以使用和去访问文件,除此之外,Linux 还提供了系统调用,它可以将文件映射到进程的地址空间,这样程序就可以通过访问内存的方式去访问文件了。那么与和相比,使用去访问文件能带来什么好处呢?

使用一个明显的好处就是减少一次 I/O 拷贝,譬如说,当我们使用读取文件时,通常的做法是这样:

这个过程实际上发生了两次 I/O 拷贝,第一次是将磁盘中的文件内容拷贝到 OS 的文件系统缓冲区,第二次是将 OS 缓冲区的数据拷贝到用户缓冲区。而使用读取文件时,只会发生第一次拷贝操作,也就是将文件内容拷贝到 OS 文件系统缓冲区,完成这个拷贝操作之后,还会执行其它一些复杂的操作,例如将相应的 OS 缓冲区映射到进程的地址空间。

尽管可以减少一次 I/O 拷贝,但由于的实现很复杂,调用将会带来额外的开销,因此在一些情况下,没有使用的必要:

访问小文件时,直接使用或将更加高效。

单个进程对文件执行顺序访问时(sequential access),使用几乎不会带来性能上的提升。譬如说,使用顺序读取文件时,文件系统会使用 read-ahead 的方式提前将文件内容缓存到文件系统的缓冲区,因此使用将很大程度上可以命中缓存。

那么,在什么情况下使用去访问文件会更高效呢?

对文件执行随机访问时,如果使用或,则意味着较低的 cache 命中率。这种情况下使用通常将更高效。

多个进程同时访问同一个文件时(无论是顺序访问还是随机访问),如果使用,那么 OS 缓冲区的文件内容可以在多个进程之间共享,从操作系统角度来看,使用可以大大节省内存。

sendfile()

Web Server 处理静态页面请求时,通常是从磁盘中读取网页的内容,然后发送给客户端:

下图是传统的读写文件流程:

正如我们前面说到的,使用读取文件时,将发生两次 I/O 拷贝。然而,数据发送的过程也发生了两次 I/O 拷贝,第一次是将用户缓冲区的数据写入内核的 socket 发送缓冲区,成功写入之后会返回,在返回之后,内核会将 socket 发送缓冲区的数据拷贝到网卡驱动。可以看到,整个过程发生了四次 I/O 拷贝操作。

然而除了考虑 I/O 拷贝带来的开销,我们还要考虑系统 context switch 带来的开销,当程序调用时,系统会从用户态切换到内核态,而当返回时,又会导致系统从内核态切换到用户态,所以调用发生两次 context switch,同理,调用也会发生两次 context switch。

Linux 提供了用来减少我们前面提到的 I/O 拷贝和 context switch 的次数:

使用发送文件时,实际发生了三次 I/O 拷贝,第一次是将磁盘中的文件内容拷贝到 OS 的文件系统缓冲区,第二次是将 OS 缓冲区的数据拷贝到 socket 的发送缓冲区,最后一次是将 socket 发送缓冲区的数据发送到网卡驱动。可以看到,与使用和发送文件相比,使用减少了一次 I/O 拷贝和两次 context switch。

如果使用的网卡支持 scatter-gather 特性,那么还可以再减少一次 I/O 拷贝:

这种情况下,使用发送文件只会发生两次 I/O 拷贝,第一次是将磁盘中的文件拷贝到 OS 的文件系统缓冲区,而第二次是将 OS 缓冲区的数据直接拷贝到网卡驱动。可以使用下面的命令查看网卡是否支持 scatter-gather 特性:

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181027G017YN00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券