万事须己运,他得非我贤。这篇文章主要讲述#yyds干货盘点#深入理解 Linux 的 I/O 系统相关的知识,希望能为你提供帮助。
传统的 System Call I/O在 Linux 系统中,传统的访问方式是通过 write() 和 read() 两个系统调用实现的,通过 read() 函数读取文件到到缓存区中,然后通过 write() 方法把缓存中的数据输出到网络端口。
read(file_fd, tmp_buf, len);
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(file_fd, tmp_buf, len);
基于传统的 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_fd, tmp_buf, len);
基于传统的 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
- 零拷贝技术。
- 多路复用技术。
- 页缓存(PageCache)技术。
页缓存读取策略:当进程发起一个读操作 (比如,进程发起一个 read() 系统调用),它首先会检查需要的数据是否在页缓存中:
- 如果在,则放弃访问磁盘,而直接从页缓存中读取。
- 如果不在,则内核调度块 I/O 操作从磁盘去读取数据,并读入紧随其后的少数几个页面(不少于一个页面,通常是三个页面),然后将数据放入页缓存中。
页缓存写策略:当进程发起 write 系统调用写数据到文件中,先写到页缓存,然后方法返回。此时数据还没有真正的保存到文件中去,Linux 仅仅将页缓存中的这一页数据标记为 “脏”,并且被加入到脏页链表中。
然后,由 flusher 回写线程周期性将脏页链表中的页写到磁盘,让磁盘中的数据和内存中保持一致,最后清理“脏”标识。在以下三种情况下,脏页会被写回磁盘:
- 空闲内存低于一个特定阈值。
- 脏页在内存中驻留超过一个特定的阈值时。
- 当用户进程调用 sync() 和 fsync() 系统调用时。
推荐阅读
- linux命令最佳alias别名工具
- 命令_awk
- #yyds干货盘点#--ELK-logstash
- #yyds干货盘点#Linux常见面试题之磁盘管理命令
- #yyds干货盘点#使用私钥登录服务器
- 关于为了吃瓜通宵7天写了一个网站却没钱买域名这件小事
- maradb 双主配置
- #yyds干货盘点#mysql索引_效率测试(包含测试sql脚本300万条数据)
- 使用VMware安装CentOS 6.4#yyds干货盘点#