块I/O¶
系统中随机访问固定大小数据片的硬件设备被称为块设备,这些固定大小的数据片就被称为块,最常见的块设备是硬盘。
另一种基本的设备类型是字符设备,比如串口和键盘。它按照字符流的方式被有序访问,随机的访问是无意义的。当在键盘上打出“char”这个单词时,屏幕必须按照输入顺序显示出来。
相比较之下,因为块设备支持随机的I/O,所以内核对于块设备的管理比字符设备要复杂许多。更重要的是块设备对于执行性能的要求很高,对于硬盘读写的优化可以带来整个系统性能的提升。
磁盘¶
磁盘是块设备的一种,常见的磁盘有机械磁盘和固态磁盘,其中机械磁盘由于需要磁道寻址,所以速度比固态磁盘要慢很多。磁盘的读写有两种方式——随机I/O和连续I/O:随机I/O是指每次读写操作都从磁盘的某个位置重新开始,而连续I/O是指每次读写操作都从同一个位置开始,并且可以通过预读的方式,减少I/O请求的次数。
磁盘读写的最小单位是扇区,但是扇区只有512B大小。为了提高读写的效率,文件系统将连续的扇区组合成逻辑块,每个逻辑块的大小最常见的是4KB——由8个扇区组成。
目录项、索引节点以及磁盘之间的关系,如下图所示:
通用块层¶
为了屏蔽不同块设备带来的差异,内核引入了通用块层,来管理不同的块设备。
通用块层是文件系统和磁盘驱动中间的一个块设备抽象层,它会对文件系统的I/O请求进行排队,再通过重新排序和请求合并,然后再发送给下一级的设备层。它有两个功能:
- 向上,为文件系统和应用程序,提供访问块设备的标准接口;向下,把不同的块设备抽象为统一个块设备。
- 管理块设备的I/O请求,并通过重新排序、请求合并等方式,优化读写磁盘的效率。
对I/O请求排序的过程,就是所谓的I/O调度。内核支持四种调度算法,分别是NONE、NOOP、CFQ以及DeadlLine。
- NONE:不做任何处理,常用在虚拟机中。
- NOOP:维护了一个先入先出的队列,制作一些最基本的请求合并,常用于SSD磁盘。
- CFQ:默认I/O调度器,为每个进程维护了一个I/O调度队列,并按照时间片来均匀分布每个进程的I/O请求。适用于运行大量进程的系统。
- DeadLine:为读、写请求创建不同的I/O队列,确保达到deadline的请求被优先处理。常用于数据库中。
异步I/O¶
Linux中最常用的输入/输出模型是同步I/O,在请求发出之后,应用程序会阻塞直到请求被满足。
异步I/O是另一种输入/输出模型,它允许应用程序在请求发出之后立即返回,而不会阻塞。glibc中的异步I/O函数接口主要包括:
函数名 | 描述 |
---|---|
aio_read | 异步读 |
aio_write | 异步写 |
aio_error | 获取异步I/O操作的状态 |
aio_return | 获取异步I/O操作的结果 |
aio_suspend | 挂起进程,直到异步I/O操作完成 |
aio_cancel | 取消异步I/O操作 |