传统IO到零拷贝

0. 前言

目前很多技术效率高都用到了零拷贝技术,Netty,Nginx,kafka等。
要想了解零拷贝技术为什么快,就要知道和以前IO的区别和进步。
I/O:输入/输出是在主存和外部设备(如磁盘驱动器、终端和网络)直接拷贝数据的过程。
例如下边是两个函数命令
1.ssize_t read(int fd, void *buf, size_t n);
2.ssize_t write(int fd, const void *buf, size_t n)

1. DMA的作用

以read函数分析:
read函数从描述符为fd的当前文件位置拷贝最多n个字节到储存器位置buf。返回-1表示一个错误,而返回0表示EOF,否则,返回值表示是实际串送字节的数量。

在没有DMA时,read函数实际上会经过下边的步骤

  1. 用户进程调用read函数,用户态切换成内核态,CPU发送指令到磁盘控制器
  2. 磁盘控制器接收指令后,把数据放入磁盘控制器的内部缓冲区,产生一个中断。
  3. CPU收到中断指令后,把磁盘缓冲区的数据读进自己的寄存器,然后把寄存器的数据写入内存。内核态切换为用户态。

CPU需要参与数据搬运过程,如果数据量大的话,太消耗CPU资源了。
由此,出现了DMA技术。
先了解下什么是DMA
DMA(直接内存访问)Direct Memory Access:在进行 I/O 设备和内存的数据传输的时候,数据搬运的工作全部交给 DMA 控制器。

同样的read命令。

  1. 用户进程调用read方法,CPU将请求发给DMA.
  2. DMA将请求发给磁盘
  3. 磁盘控制器接收指令后,把数据放入磁盘控制器的内部缓冲区,产生一个中断。
  4. DMA 收到磁盘的信号,将磁盘控制器缓冲区中的数据拷贝到内核缓冲区中.
  5. 当DMA读取足够多的数据,就发送中断信号给CPU
  6. CPU 收到 DMA 的信号,知道数据已经准备好,于是将数据从内核拷贝到用户空间,系统调用返回。
    CPU不在参与数据搬运的工作,而是由DMA完成。
    在这里插入图片描述DMA技术的出现减少了CPU的占用和消耗,但是指令还是由CPU发出,一个read命令,两次上下文切换,两次数据拷贝

2. 文件传输

2.1. read+write

以正常一次读取磁盘文件发送数据为例,需要调用下列命令

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

发生了4次用户态和内核态的上下文切换(两次调用都是:用户态->内核态,内核完成任务后:内核态->用户态),4次数据的拷贝(磁盘数据->操作系统内核缓冲区->用户缓冲区->内核的socket的缓冲区->网卡的缓冲区)

文件传输的性能

  1. 减少上下文切换次数(每一次系统调用,都会经过两次上下文切换)
  2. 减少数据拷贝次数

优化方案

  1. mmap+write
  2. sendfile

2.2. mmap+witer

buf = mma(file,len)
mmap函数会直接吧内核缓冲区里的数据映射到用户空间,操作系统不需要进行任何数据的拷贝操作

发生了4次用户态和内核态的上下文切换(两次调用都是:用户态->内核态,内核完成任务后:内核态->用户态),3次数据的拷贝(磁盘数据->操作系统内核缓冲区(应用进程和操作系统共享)->内核的socket的缓冲区->网卡的缓冲区)

2.3. sendfile

linux内核版本2.1后
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
前两个参数分别是目的端和源端的文件描述符,后面两个参数是源端的偏移量和复制数据的长度,返回值是实际复制数据的长度。

发生了2次用户态和内核态的上下文切换(一次调用),3次数据的拷贝(磁盘数据->操作系统内核缓冲区->内核的socket的缓冲区->网卡的缓冲区)

网卡支持 SG-DMA技术,从 Linux 内核 2.4 版本开始起支持
2次数据的拷贝(磁盘数据->操作系统内核缓冲区->网卡的缓冲区)
这就是零拷贝,2次上下问切换,2次数据拷贝,全是DMA拷贝,没有CPU拷贝。

2.4. 对比

在这里插入图片描述

3. PageCache(磁盘高速缓存)

当文件传输时,第一步都是需要先把磁盘文件数据拷贝待内核缓存区(PageCache)
读写磁盘相比与读写内存速度慢太多了,所以要用读写磁盘替换为读写内存。

  1. PageCache来缓存最近被访问的数据,当空间不足时淘汰最久未被访问的缓存。
  2. PageCache使用可预读功能

因为局部性(刚访问的数据,短时间内再次访问的概率很高),所以缓存到PageCache里
PageCache来缓存最近被访问的数据,当空间不足时淘汰最久未被访问的缓存。

因为磁盘数据读取时(通过磁头旋转到数据所在的扇区,然后开始顺序读取)。旋转磁头非常耗时。预读功能(kafka顺序读写,很快)
读磁盘数据->先在PageCache找,找不到,再从磁盘中读取,缓存到PageCache。

4. 大文件传输

PageCache被大文件占据,其他热点小文件无法使用pageCache,磁盘读写下降
PageCache没有享受到缓存带来的好处,却耗费了DMA多拷贝到PageCache一次

异步IO,前半部分,内核向磁盘发起请求,不等数据就位,就返回;
后半部分,内核数据拷贝到缓冲区后,进程将接收到内核的通知,处理数据,绕开PageCache,直接IO;
异步IO+直接IO(绕过PageCache)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值