前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Netty之美--零拷贝

Netty之美--零拷贝

作者头像
早安嵩骏
发布2020-08-11 16:44:04
5190
发布2020-08-11 16:44:04
举报
文章被收录于专栏:程序猿人程序猿人

零拷贝(Zero-copy)是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。

概念:
上下文切换

当用户程序向内核发起系统调用时,CPU将用户进程从用户态切换到内核态;当系统调用返回时,CPU将用户进程从内核态切换回用户态。具体可以参考:你该懂得操作系统知识—内核态和用户态

CPU拷贝

由CPU直接处理数据的传送,数据拷贝时会一直占用 CPU 的资源。

DMA

DMA(Direct Memory Access)中文译为:直接存储器访问,是一种I/O控制方式,它的特点是:

  1. 数据传输的基本单位是数据块,即在CPU与I/O设备之间,每次传送至少一个数据块;
  2. 所传送的数据是从设备直接送入内存的,或者相反;
  3. 仅在传送一个或多个数据块的开始和结束时,才需CPU干预(很少,可忽略),整块数据的传送是在控制器的控制下完成的。
DMA拷贝

由CPU向DMA磁盘控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给 CPU,从而减轻了CPU资源的占有率。

两个读写操作系统函数
代码语言:javascript
复制
ssize_t read(int fd, void *buf, size_t count)
ssize_t write(int fd, const void *buf, size_t count);
传统I/O

如上图,假设需求是将一个磁盘文件发布到网络上。具体步骤是:

  1. 应用程序调用系统方法read发起读文件操作,同时CPU由用户态转为内核态,
  2. 系统通过DMA控制器将文件拷贝到内核缓冲区,该操作基本不需要CPU参与;
  3. CPU将数据从内核缓冲区拷贝到用户缓冲区,发生一次CPU拷贝,同时CPU由内核态转为用户态;到此为止,系统调用read方法返回。
  4. 程序继续调用write方法,同时CPU由用户态转为内核态,CPU将数据又从用户缓冲区拷贝到socket buffer;
  5. 数据通过DMA被拷贝到协议引擎,如网卡等,同时write方法返回,CPU由内核态转为用户态。

总共需要2次CPU拷贝、2次DMA拷贝,4次上下文切换,其中read和write各占一半;

零拷贝
1. 直接I/O

直接I/O就是应用程序直接访问磁盘数据,而不经过内核缓冲区,这样做的目的是减少一次从内核缓冲区到用户程序缓存的数据复制。通常使用在数据库管理系统这类应用,它们更倾向于选择它们自己的缓存机制,因为数据库管理系统往往比操作系统更了解数据库中存放的数据,数据库管理系统可以提供一种更加有效的缓存机制来提高数据库中数据的存取性能。

2. mmap

mmap的原理:将用户态缓存空间和内核态缓存空间做一个映射,如此程序在用户态就可以直接操作并使用内核缓存空间的数据。

mmap函数:int munmap(void* start,size_t length);

如图,具体步骤:

  1. 程序调用系统方法mmap(图上有误),使用DMA的方式将磁盘数据读取到内核缓冲区,然后通过内存映射的方式,将用户缓冲区映射到内核读缓冲区的内存地址,此处不再需要将内核空间的数据拷贝到用户空间;
  2. 依然调用write方法将用户空间的数据拷贝到socket buffer,同时使用DMA将数据拷贝到硬件;

相比传统I/O减少了一次CPU拷贝,总共需要2次DMA拷贝,1次CPU拷贝,4次上下文切换。

3. sendfile

sendfile原理:Linux2.1版本提供了sendFile函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入socket buffer。同时,由于和用户态完全无关,就减少了一次CPU拷贝。

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

如图,步骤是:

  1. 程序调用sendfile方法(上图有误),使用DMA的方式将磁盘数据读取到内核缓冲区;
  2. CPU拷贝数据到socket buffer,然后使用DMA将数据拷贝到硬件。

相比mmap少了1次上下文切换,总共需要2次DMA拷贝,1次CPU拷贝,2次上下文切换。但是缺点也是很明显的,由于完全没有经过用户态,sendfile只能简单的传送数据而不能对其进行修改;

4. 升级版sendfile

Linux2.4版本sendFile做了一些优化,避免了从内核缓冲区拷贝到socket buffer的操作,直接拷贝到协议栈,从而再一次减少数据拷贝。(其实这里是有一次CPU拷贝的,从kernel buffer-> socket buffer,但是拷贝信息很少,消耗低,可以忽略)

总共需要2次DMA拷贝,近0次CPU拷贝,2次上下文切换。

mmap和sendFile的区别

  1. mmap适合小数据量读写,sendFile适合大文件传输;
  2. mmap需要4次上下文切换,3次数据拷贝;sendfile需要2次上下文切换,近2次数据拷贝;
  3. 使用mmap的时候,可以对数据做修改操作,而sendfile不行;
5. 补充1:splice

splice函数是linux系统提供的高级I/O函数,同sendfile系统调用函数一样,也是零拷贝操作函数。splice函数用于在两个文件描述符之间的移动数据。

6. 补充2:tee

tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。与splice函数不同,它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作。

总的来看,所谓的零拷贝,是从操作系统角度去看,是没有CPU拷贝,只有kernel buffer一份数据。零拷贝不仅带来了更少的数据复制,还能带来其他的性能优势,例如更少的上下文切换,更少的CPU缓存伪共享以及无CPU校验和计算;

Netty应用
  1. Netty的接受和发送ByteBuffer采用的是DirectBuffer,使用堆外内存进行Socket读写,不需要进行字节缓存区的二次拷贝。如果使用传统的堆内存(Heap Buffers)进行Socket读写,JVM会将堆内存拷贝到一份DirectBuffer中,然后再写入Socket。所以Netty的实现方式直接少了一次缓冲区的内存拷贝;至于为什么一定要使用DirectBuffer进行Socket读写是因为,当我们把一个地址通过JNI传递给底层的C库的时候,要求地址传输的内容不能失效,堆内存由于存在GC的原因有可能失效,所以一定要是要堆外内存。
  2. Netty实现了CompositeByteBuf,它对外将多个ByteBuf封装成一个ByteBuf对外提供统一封装后的ByteBuf接口,如此我们添加ByteBuf不需要再做内存拷贝;
  3. 第三种“零拷贝”体现在文件传输上,Netty文件传输类DefaultFileRegion通过Java NIO的transferTo方法将文件发送到目标Channel中。transferTo这一点正是应用了Linux的mmap或者sendFile方法实现了零拷贝。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念:
    • 上下文切换
      • CPU拷贝
        • DMA
          • DMA拷贝
            • 两个读写操作系统函数
            • 传统I/O
            • 零拷贝
              • 1. 直接I/O
                • 2. mmap
                  • 3. sendfile
                    • 4. 升级版sendfile
                      • 5. 补充1:splice
                        • 6. 补充2:tee
                        • Netty应用
                        相关产品与服务
                        数据库管理
                        数据库管理(Database Management Center,DMC)是一个高效,安全,可靠的数据库一站式管理平台。DMC 提供可视化的库管理、实例会话管理、SQL 窗口、SQL 安全审计、SQL 变更审批、实时监控、操作审计等数据库管理能力,集成诊断优化和数据可视化分析能力,从而简化和规范数据库管理操作、降低数据库运维门槛、提升运维效率。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档