深入理解 Linux的 I/O 系统
时间:2021-11-15 14:12:56
[导读]↓推荐关注↓传统的SystemCallI/O在Linux系统中,传统的访问方式是通过write()和read()两个系统调用实现的,通过read()函数读取文件到到缓存区中,然后通过write()方法把缓存中的数据输出到网络端口。read(file_fd, tmp_buf, le...
传统的 System Call I/O
在 Linux 系统中,传统的访问方式是通过 write() 和 read() 两个系统调用实现的,通过 read() 函数读取文件到到缓存区中,然后通过 write() 方法把缓存中的数据输出到网络端口。
write(socket_fd, tmp_buf, len); 下图分别对应传统 I/O 操作的数据读写流程,整个过程涉及 2 次 CPU 拷贝、2 次 DMA 拷贝,总共 4 次拷贝,以及 4 次上下文切换。
-
CPU 拷贝:
由 CPU 直接处理数据的传送,数据拷贝时会一直占用 CPU 的资源。
-
DMA 拷贝:
由 CPU 向DMA磁盘控制器下达指令,让 DMA 控制器来处理数据的传送,数据传送完毕再把信息反馈给 CPU,从而减轻了 CPU 资源的占有率。
-
上下文切换:
当用户程序向内核发起系统调用时,CPU 将用户进程从用户态切换到内核态;
当系统调用返回时,CPU 将用户进程从内核态切换回用户态。
读操作
当应用程序执行 read 系统调用读取一块数据的时候,如果这块数据已经存在于用户进程的页内存中,就直接从内存中读取数据。
基于传统的 I/O 读取方式,read 系统调用会触发 2 次上下文切换,1 次 DMA 拷贝和 1 次 CPU 拷贝。
- 用户进程通过 read() 函数向 Kernel 发起 System Call,上下文从 user space 切换为 kernel space。
- CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到 kernel space 的读缓冲区(Read Buffer)。
- CPU 将读缓冲区(Read Buffer)中的数据拷贝到 user space 的用户缓冲区(User Buffer)。
- 上下文从 kernel space 切换回用户态(User Space),read 调用执行返回。
写操作
当应用程序准备好数据,执行 write 系统调用发送网络数据时,先将数据从用户空间的页缓存拷贝到内核空间的网络缓冲区(Socket Buffer)中,然后再将写缓存中的数据拷贝到网卡设备完成数据发送。
基于传统的 I/O 写入方式,write() 系统调用会触发 2 次上下文切换,1 次 CPU 拷贝和 1 次 DMA 拷贝。
- 用户进程通过 write() 函数向 kernel 发起 System Call,上下文从 user space 切换为 kernel space。
- CPU 将用户缓冲区(User Buffer)中的数据拷贝到 kernel space 的网络缓冲区(Socket Buffer)。
- CPU 利用 DMA 控制器将数据从网络缓冲区(Socket Buffer)拷贝到 NIC 进行数据传输。
- 上下文从 kernel space 切换回 user space,write 系统调用执行返回。
网络 I/O
磁盘 I/O
高性能优化的 I/O
- 零拷贝技术。
- 多路复用技术。
- 页缓存(PageCache)技术。
- 如果在,则放弃访问磁盘,而直接从页缓存中读取。
- 如果不在,则内核调度块 I/O 操作从磁盘去读取数据,并读入紧随其后的少数几个页面(不少于一个页面,通常是三个页面),然后将数据放入页缓存中。
-
空闲内存低于一个特定阈值。
-
脏页在内存中驻留超过一个特定的阈值时。
-
当用户进程调用 sync() 和 fsync() 系统调用时。
存储设备的 I/O 栈
由图可见,从系统调用的接口再往下,Linux 下的 IO 栈致大致有三个层次:
-
文件系统层,以 write 为例,内核拷贝了 write 参数指定的用户态数据到文件系统 Cache 中,并适时向下层同步。
-
块层,管理块设备的 IO 队列,对 IO 请求进行合并、排序(还记得操作系统课程学习过的 IO 调度算法吗?)。
-
设备层,通过 DMA 与内存直接交互,完成数据和具体设备之间的交互。
I/O Buffering
如图,当程序调用各类文件操作函数后,用户数据(User Data)到达磁盘(Disk)的流程如图所示。
作者:范桂飓 整理:极客重生- EOF -https://is-cloud.blog.csdn.net/article/details/105897963





