Skip to content

块I/O

系统中随机访问固定大小数据片的硬件设备被称为块设备,这些固定大小的数据片就被称为块,最常见的块设备是硬盘。

另一种基本的设备类型是字符设备,比如串口和键盘。它按照字符流的方式被有序访问,随机的访问是无意义的。当在键盘上打出“char”这个单词时,屏幕必须按照输入顺序显示出来。

相比较之下,因为块设备支持随机的I/O,所以内核对于块设备的管理比字符设备要复杂许多。更重要的是块设备对于执行性能的要求很高,对于硬盘读写的优化可以带来整个系统性能的提升。

磁盘

磁盘是块设备的一种,常见的磁盘有机械磁盘和固态磁盘,其中机械磁盘由于需要磁道寻址,所以速度比固态磁盘要慢很多。磁盘的读写有两种方式——随机I/O和连续I/O:随机I/O是指每次读写操作都从磁盘的某个位置重新开始,而连续I/O是指每次读写操作都从同一个位置开始,并且可以通过预读的方式,减少I/O请求的次数。

磁盘读写的最小单位是扇区,但是扇区只有512B大小。为了提高读写的效率,文件系统将连续的扇区组合成逻辑块,每个逻辑块的大小最常见的是4KB——由8个扇区组成。

目录项、索引节点以及磁盘之间的关系,如下图所示:

磁盘

通用块层

为了屏蔽不同块设备带来的差异,内核引入了通用块层,来管理不同的块设备。

通用块层是文件系统和磁盘驱动中间的一个块设备抽象层,它会对文件系统的I/O请求进行排队,再通过重新排序和请求合并,然后再发送给下一级的设备层。它有两个功能:

  1. 向上,为文件系统和应用程序,提供访问块设备的标准接口;向下,把不同的块设备抽象为统一个块设备。
  2. 管理块设备的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操作