I/O 指的是相对内存而言的 input 和 output。从文件、数据库、网络向内存中写入数据叫做 input;从内存向文件、数据库、网络中输出数据叫做 output。Linux 系统 I/O 分为内核准备数据和将数据从内核拷贝到用户空间两个阶段。
I/O 的两种方式(缓存 I/O & 直接 I/O)
缓存 I/O
缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓冲区复制到应用程序的地址空间(用户空间)。
读操作:操作系统检查内核空间的缓冲区有没有需要的数据,如果已经缓存了,那么就直接从缓存中返回,也就是将数据复制到应用程序的用户空间;否则从磁盘中读取数据至内核空间的缓冲区,再将内核空间缓冲区的数据返回。
写操作:将数据从用户空间复制到内核空间的缓冲区,这时对用户程序来说写操作就已经完成。至于什么时候将数据从内核空间写到磁盘中,这步由操作系统决定,除非显示地调用了 sync 同步命令。
缓存 I/O 的优点:
- 在一定程度上分离了内核空间和用户空间,保护系统本身的运行安全;
- 可以减少读盘的次数,从而提高性能。
缓存 I/O 的缺点:
在缓存 I/O 机制中,DMA 方式可以将数据直接从磁盘读到内核空间的页缓存中,或者将数据从内核空间的页缓存中直接写回到磁盘上,而不能直接在应用程序地址空间(用户空间)和磁盘之间进行数据传输。这样,数据在传输过程中需要在应用程序地址空间(用户空间)和页缓存(内核空间)之间进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是比较大的。
直接 I/O
直接 IO 就是应用程序直接访问磁盘,而不经过内核缓冲区,这样做的目的是减少一次从内核缓冲区到用户程序地址空间的数据复制操作。
例如数据库管理系统这类应用,它们更倾向于选择自己的缓存机制,因为数据库管理系统往往比操作系统更了解数据库中存放的数据。数据库管理系统可以提供一种更加高效的缓存机制来提高数据库中存取数据的性能。
内存映射 memory mapped
应用程序如何跳过内核直接访问磁盘呢?这需要用到 “内存映射” 技术。
内存映射又被称为内存映射文件、文件映射,是一段用户空间地址逐字节地映射到磁盘文件地址中的技术,它使得应用程序处理映射部分的数据如同访问主内存(也就是内核空间)。为什么叫内存映射或文件映射呢?这里的内存和文件指的就是磁盘的一部分空间,这部分空间显然与普通的磁盘是不一样的,它被称作 “虚拟内存” 。虚拟内存在功能上如同主内存(内核空间),但是在读写速度上差距是不小的。
虚拟内存
内存映射技术依赖于虚拟内存。虚拟内存是计算机系统内存管理的一种技术,它把内存扩展到磁盘,也就是把磁盘的一部分空间当作内存来用,这部分空间在级别上等同于系统内核空间。虚拟内存技术有效地扩展了内存空间,适用于大文件的存储操作。
比方说一个电脑内存就 4G,用户空间被分配 3G,内核空间被分配 1G。一个大文件体积是 2G,这么大的东西要通过系统内核空间存储到磁盘就比较费劲了。电脑磁盘是 256G,如果使用虚拟内存技术把磁盘的一部分 2G 空间映射到用户空间的文件处理进程的地址上,内存管理程序只需要把大文件复制从用户空间复制到对应的虚拟内存中即可。这就是虚拟内存的一个使用场景,它避免通过内核空间的二次复制即可将大文件存储至磁盘。
内存映射流程
用户空间首先调用 mmap 函数将一段空闲且连续的磁盘地址初始化为虚拟内存空间并将虚拟内存空间与用户空间形成映射关系。形成映射关系的用户空间实际指的是某一进程占用的用户空间,内存映射发生在进程中。多个进程可以同时映射到同一块虚拟内存空间,虚拟内存空间又被称作共享对象。
详细步骤如下:
1、在磁盘上寻找一段空闲且连续的空间
2、初始化该空间为内存区域
3、将该内存区域插入到进程的地址区域链表中
4、通过待映射的文件指针、文件描述符调用内核空间的 mmap 函数
5、此 mmap 函数通过虚拟文件系统的 innode 模块定位虚拟内存区域的磁盘地址
6、通过 remap_pfn_range 建立页表,实现用户空间与虚拟内存的映射关系
7、进程读写操作访问虚拟内存空间
8、如果进程写操作修改了虚拟内存空间的数据形成脏页
9、一定时间后,系统会自动将脏页写入磁盘
直接 IO 的缺点:
如果访问的数据不在应用程序(用户空间)缓存中,那么每次数据都会直接从磁盘加载,这种直接加载比较缓慢。从磁盘加载与从内存加载相比,肯定是慢很多。通常直接 IO 与异步 IO 结合使用,会得到比较好的性能。(异步 IO:当访问数据的线程发出请求之后,线程会接着去处理其它事,而不是阻塞等待。)
PIO 与 DMA
有必要简单地说说慢速 I/O 设备和内存之间的数据传输方式。
PIO
很早以前,磁盘和内存之间的数据传输是需要 CPU 控制的,也就是说如果我们读取磁盘文件到内存中,数据要经过 CPU 存储转发,这种方式称为 PIO。显然这种方式非常不合理,需要占用大量的 CPU 时间来读取文件,造成文件访问时系统几乎停止响应。
DMA
后来,DMA(直接内存访问,Direct Memory Access)取代了 PIO,它可以不经过 CPU 而直接进行磁盘和内存的数据交换。在 DMA 模式下,CPU 只需要向 DMA 控制器下达指令,让 DMA 控制器来处理数据的传送即可。DMA 控制器通过系统总线来传输数据,传送完毕再通知 CPU,这样就在很大程度上降低了 CPU 占有率,显著提升了系统性能。DMA 的传输速度与 PIO 的差异其实并不十分明显,因为这主要取决于慢速设备的速度,前者的关键优势是释放了 CPU 的占用。
可以肯定的是,PIO 模式的计算机我们现在已经很少见到了。