缓存区
用户缓存区
用户缓冲区的目的是为了减少系统调用次数,从而降低操作系统在用户态与核心态切换所耗费的时间
程序在读取文件时,会先申请一块内存数组,称为buffer,然后每次调用read,读取设定字节长度的数据,写入buffer。(用较小的次数填满buffer)。之后的程序都是从buffer中获取数据,当buffer使用完后,在进行下一次调用,填充buffer。
内核缓存区
读磁盘数据时 先把数据从磁盘读到缓冲区。 然后将内核缓冲区的数据复制到进程缓冲区中。
当内额缓冲区没有数据时,内核会把对数据块的请求,加入到请求队列,然后把进程挂起,为其它进程提供服务。等io完成时 才会通知进程。
不同的io模型,在调度和使用内核缓冲区的方式上有所不同
数据拷贝过程
仅CPU
应用程序调用read(),CPU发起IO请求,磁盘控制器搬运数据。发起IO中断
CPU拷贝数据: 磁盘缓冲区-内核缓冲区-用户缓冲区
CPU&DMA方法
直接内存访问(Direct Memory Access),是一种硬件设备绕开CPU独立直接访问内存的机制。所以DMA在一定程度上解放了CPU,把之前CPU的杂活让硬件直接自己做了,提高了CPU效率。
目前支持DMA的硬件包括:网卡、声卡、显卡、磁盘控制器等。
有了DMA,CPU直接和内核缓冲区交换数据。
普通数据交互过程
读过程:
read切换到内核态
DMA读数据
CPU将数据从内核缓冲区复制到用户缓冲区
read返回切换到用户态
写过程:
write切换内核态
CPU将数据从用户缓冲区拷贝到内核缓冲区
DMA将数据拷贝到socket缓冲区
write返回切换到用户态
零拷贝
mmap方式
将内核中读缓冲区地址与用户空间缓冲区地址进行映射,从而实现内核缓冲区与用户缓冲区的共享。
CPU只需让数据从内核缓冲区拷贝到套接字缓冲区。减少了一次拷贝。
sendfile方式
sendfile方式只使用一个函数就可以完成之前的read+write 和 mmap+write的功能,这样就少了2次状态切换,由于数据不经过用户缓冲区,因此该数据无法被修改。
sendfile调用时进入内核态 DMA从disk拷贝来的数据放入内核缓冲区 然后CPU将内核缓冲区的数据拷贝到套接字缓冲区 DMA将套接字缓冲区拷贝到网卡 sendfile返回 切换到用户态。
sendfile+DMA
升级后的sendfile将内核空间缓冲区中对应的数据描述信息(文件描述符、地址偏移量等信息)记录到socket缓冲区中。
DMA控制器根据socket缓冲区中的地址和偏移量将数据从内核缓冲区拷贝到网卡中,从而省去了内核空间中仅剩1次CPU拷贝。