IO与零拷贝

IO与零拷贝
零拷贝基本介绍
  • 零拷贝时网络编程的一个关键优化点
  • 在Java程序中,常用的零拷贝又mmap(内存映射)和sendFile。那么在OS中的设计时如何,我们需分析mmap和sendFile对比
  • 最后通过案例分析
用户进程与操作系统关系
  • 我们先看一张图解释“用户进程和操作系统的关系”
    请添加图片描述
  • 如上计算机系统运行时候的简化说明,在操作系统中,有用户空间,内核空间,
    • 运行在操作系统傻姑娘的进程大多是用户进程,运行在用户空间,在资源管理器里面可以看得到
    • 吧操作系统运行的空间成为系统空间
  • 为何划分用户进程和系统进程,我们需要先解释内核态(Kernel mode)和用户态(user mode),内核态可以访问系统资源,比如:
    • 处理器CPU
    • 输入输出IO,包括硬盘,内存,终端等
    • 进程管理
    • 内存
    • 设备:键盘鼠标啥的
  • 以上资源都是用户进程无法直接访问。只能通过操作系统去访问,所以也罢操作系统提供的这些功能称为“系统调用”,我们文件读写,和终端控制都是通过内核进行的。
用户进程缓冲区
  • 按照以上的设计,用户进程访问系统资源需要切换到内核态,这对于的一下特殊的内存环境,必须在系统调用之前建立好,而在系统调用结束后,CPU会从内核模式切换到用户模式,而且对照又必须回复成用户进程的上下文,这种切换就会有大量的耗时。
  • 因此我们在程序读取文件的时候,会先申请一块内存,成为buffer,每次调用read读取指定字节长度的数据,写入buffer。之后的程序都从buffer中获取数据。当buffer用完后,在进行下一次调用,填充buffer。
  • 所以说,用户缓冲区目的时为了减少系统调用次数,从而减低操作系统在用户态和核心态切换的时间。如下图对比请添加图片描述

请添加图片描述

内核缓冲区
  • 内核也有自己的缓冲区
  • 当用户进程要从磁盘读数据,内核一般步直接读磁盘,而是将CIO缓冲区中数据复制到进程缓冲区
  • 单如果内核缓冲区中没用数据,内核会吧对数据块的请求加入到请求队列,然后吧进程挂起,为其他进程提供服务。
  • 等数据一句读取到内核缓冲区时候,内核缓冲区中数据读取到用户进程,才会通知进程。
  • 此处read是吧数据从高内核缓冲区复制到进程缓冲区。write是吧进程缓冲区复制到内核缓冲区。
  • 因此内核缓冲区是为了在OS级别,提高磁盘IO的效率,优化磁盘读写操作。
    请添加图片描述
传统IO模型内存拷贝请添加图片描述
  • 流程如下:

    • DMA拷贝,从磁盘到内核,direct memory access 直接内存拷贝 不使用CPU
    • CPU将内核buffer 中的数据拷贝到 用户buffer
    • 接着通过socket传递,还是user buffer 拷贝到内核中的socket buffer
    • 最后从内核socket buffer 到引擎,就是对应的协议比如TCP/IP
  • 如上流传中

    • 状态切换3 次
    • 拷贝4次
mmap优化

请添加图片描述

  • mmap优化通过内存映射的方式,将文件映射到内核缓冲区,同时用户空间可以贡献内核空间的数据,这样,在进行网络传输的时候,减少了内核空间到用户空间的拷贝
  • mmap的优化中
    • 状态切换3次
    • 数据拷贝3次 比传统IO的形式少了一次拷贝
sendFile 优化

请添加图片描述

  • Linux 2.1版本提供了sendFile函数,基本原理如上图,数据根本不进过用户态,直接从高内核缓冲区到SocketBuffer,同时由于和用户态没关系,减少了一次上下文切换
  • sendFile的优化中
    • 上下文切换2次 比mmap还少了一次上下文切换
    • 数据拷贝3 次 与mmap一样
Linux 2.4 的sendFile

请添加图片描述

  • linux 在2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到Socketbuffer的操作,直接拷贝到协议栈,从而再次减少了一次数据拷贝
  • 入上图中,其实只有一次CPU拷贝, kernel buffer —> socket buffer ,但是拷贝的信息不多,比如length,offset 等消耗低可以忽略
  • 优化后的sendFile
    • 上下文切换2次
    • 数据拷贝 2 次比2.1版本的sendFile 还少一次拷贝
零拷贝的理解
  • 零拷贝从操作系统角度看是没用CPU拷贝,而不是一次拷贝都没有,可见在以上几个IO优化中mmap, 2.1版本的sendFile, 2.4版本的sendFile中,只有2.4版本的sendFile实现正在意义上的零拷贝
  • 从操作系统角度看,内核缓冲区之间没有数据是重复的(只有kernel buffer 一份数据),以上kernel buffer 到 socket buffer的复制都是在内核中,他们是共享的一块内存,只是同步数据的数据同步了一个offset和length
  • 零拷贝不仅带来更少的数据复制,还带来了其他性能的优势,例如更少的上下文切换,更少的CPU缓存伪共享以及无CPU校验和计算
mmap和 sendFile的区别
  • mmap 适合小数据量的读写, sendFile适合大文件的传输
  • mmap需要3次上下文切换,3次拷贝,sendFile需要 2次上下文切换,2次拷贝
  • sendFile 可以利用DMA的方式减少CPU的拷贝,mmap不能,他必须从内核拷贝到Socket buffer
NIO零拷贝的DEMO
**
 * @author liaojiamin
 * @Date:Created in 18:36 2022/8/16
 */
public class NIOFileChannelTransferFormZeroCopy {
    private static final String filePath = "E:\\learn\\问题汇总\\MYSQL.md";
    private static final String filePathResult = "E:\\learn\\问题汇总\\MYSQL_1.md";
    public static void main(String[] args) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(filePath);
        FileOutputStream fileOutputStream = new FileOutputStream(filePathResult);
        FileChannel inputFileChannel = fileInputStream.getChannel();
        FileChannel outputFileChannel = fileOutputStream.getChannel();
        //从目标通道中复制数据到当前通道
//        outputFileChannel.transferFrom(inputFileChannel, 0, inputFileChannel.size());
        //把数据从当前通道复制给目标通道
        inputFileChannel.transferTo(0, inputFileChannel.size(), outputFileChannel);
        inputFileChannel.close();
        outputFileChannel.close();
        fileInputStream.close();
        fileOutputStream.close();
    }
}
  • 使用NIO零拷贝的传递方式transferTo或者transferFrom
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值