基础之零拷贝相关知识总结记录
IO操作方式、零拷贝等相关知识总结记录
IO操作方式:轮询方式IO操作、中断方式IO操作、DMA方式IO操作、通道方式IO操作。
传统IO(read、write)
零拷贝:
- 直接IO
- mmap + write
- sendfile
- sendfile + DMA gather copy
- splice
IO操作方式
轮询方式的IO操作
对IO设备进行轮询,如果可以进行处理,就进行处理。
缺点:轮询方式会消耗CPU,效率比较低。
中断方式的IO操作
当IO设备完成IO操作后,会向处理器发送中断信号,处理器收到中断信号后进行IO操作处理。
优点:中断方式的IO操作提高了CPU利用率。
缺点:
- 如果IO设备过多,可能会造成中断次数过多,CPU无法响应中断或者数据丢失等问题。
- 如果IO设备缓冲区较小,则缓冲区满后会发生中断,也会产生较多中断,占用CPU进行处理。
DMA方式的IO操作
DMA:直接内存存取,可以使得数据在内存和IO设备之间直接进行成块的传输。DMA会在数据传输完成后,通过中断通知CPU,CPU进行处理。
优点:IO设备直接和内存进行数据交换,效率高。
通道方式的IO操作
通道方式的IO操作可参考:https://www.cnblogs.com/niuyourou/p/12588407.html
传统IO操作方式
Linux系统中,传统IO操作是通过write()
和read()
两个系统调用实现的。
传统IO操作的读取操作过程如下:
- 用户进程调用
read()
函数进行系统调用,进程从用户态切换到内核态。 - 操作系统将IO请求交给DMA,DMA将数据从磁盘拷贝到内核缓冲区。
- CPU将内核缓冲区中的数据拷贝到用户缓冲区。
- 进程从内核态切换到用户态。
传统IO操作的写操作过程如下:
- 用户进程调用
write()
函数进行系统调用,进程从用户态切换到内核态。 - CPU将用户缓冲区中的数据拷贝到内核缓冲区中。
- DMA将数据从内核缓冲区拷贝到网卡。
- 进程从内核态切换到用户态。
传统的IO操作需要两次系统调用,产生了4次上下文切换、2次CPU拷贝、2次DMA拷贝。
零拷贝方式的IO操作
传统IO操作中上下文切换次数多,拷贝次数多,是造成IO效率低下的原因,提高效率的方法是想办法减少上下文切换次数以及数据拷贝次数。
直接IO
直接IO的方式,让用户态进程直接访问硬件设备,数据传输直接跨过内核。但是这种直接IO只适用于不需要内核进行处理的数据。另外IO设备的低速也会导致IO操作效率低,可以使用异步IO配合。
mmap + write
mmap是linux中的内存映射,可以将内核缓冲区的地址和用户缓冲区地址进行映射,这样将内核缓冲区的内容共享给用户进程,无需进行将数据从内核缓冲区拷贝到用户缓冲区的操作,减少一次CPU拷贝。
mmap + write的IO操作过程如下:
- 用户进程调用
mmap()
函数进行系统调用,进程从用户态切换到内核态。 - DMA将数据从磁盘拷贝到内核缓冲区。
- 内核缓冲区地址和用户缓冲区地址进行映射,无需进行数据从内核缓冲区拷贝到用户缓冲区的操作。
- 上下文从内核态切换到用户态,
mmap()
调用返回。 - 用户进程调用
write()
函数进行系统调用,进程从用户态切换到内核态。 - CPU将内核缓冲区的数据拷贝到内核缓冲区(网络缓冲区)中。
- DMA从内核缓冲区将数据拷贝到网卡中。
- 上下文从内核态切换到用户态,
write()
调用返回。
mmap+write方式,上下文切换有4次、CPU拷贝1次、DMA拷贝2次。
sendfile
sendfile方式的IO操作过程如下:
- 用户进程调用
sendfile()
函数进行系统调用,进程从用户态切换到内核态。 - DMA将数据从硬盘拷贝到内核缓冲区。
- CPU将数据从内核缓冲区拷贝到内核缓冲区(网络缓冲区)。
- DMA将数据冲内核缓冲区拷贝到网卡。
- 上下文从内核态切换到用户态,
sendfile()
调用返回。
sendfile的方式,上下文切换2次、CPU拷贝1次、DMA拷贝2次。sendfile方式传输数据,用户进程不能对数据进行修改。
sendfile + DMA gather copy
sendfile方式中有一次CPU拷贝,可以使用sendfile+DMA gather copy将这一次CPU拷贝给去掉。
sendfile + DMA gather copy方式的IO操作过程如下:
- 用户进程调用
sendfile()
函数进行系统调用,进程从用户态切换到内核态。 - DMA将数据从硬盘拷贝到内核缓冲区。
- CPU将内核缓冲区的文件描述符和数据长度拷贝到内核缓冲区(网络缓冲区)。
- DMA利用已拷贝的文件描述符和数据长度,将数据从内核缓冲区拷贝到网卡。
- 上下文从内核态切换到用户态,
sendfile()
调用返回。
sendfile + DMA gather copy的方式上下文切换2次、CPU拷贝0次、DMA拷贝2次。这种方式传输数据,用户进程不能对数据进行修改,并且依赖硬件的支持。
splice
splice方式的IO操作过程如下:
- 用户进程调用
splice()
函数进行系统调用,进程从用户态切换到内核态。 - DMA将数据从硬盘拷贝到内核缓冲区。
- CPU在内核缓冲区和内核缓冲区(网络缓冲区)之间建立管道pipeline,
- DMA将数据从内核缓冲区拷贝到网卡。
- 上下文从内核态切换到用户态,
splice()
调用返回。
splice方式上下文切换2次、CPU拷贝0次、DMA拷贝2次。这种方式传输数据,用户进行不能对数据进行修改,但是不依赖硬件支持。使用了Linux中的管道缓冲机制。