块设备驱动程序负责实现对块设备数据的读写功能。内核代码统一使用缓冲块间接和块设备(如磁盘)交换数据,缓冲区数据通过块设备驱动程序和块设备交换数据。
块设备的管理 块设备表内核通过一张块设备表blk_dev[]管理各种块设备,每个表项对应一个块设备,并为每一个块设备维护一个请求队列。
- current_request:指向设备请求队列第一个节点,请求队列通过链表实现。
- request_fn:函数指针,用于处理请求队列里面的读写请求。
内核为每个块设备维护一个请求队列,请求队列中请求等待被处理,current_request指向设备请求队列。
- 请求项
用于描述请求操作,包含读写磁盘扇区位置信息、缓冲区地址信息、操作类别(读/写)信息等。
请求项数据结构:
- 请求项数组
一块连续内存,被分割为请求项大小的块,形成数组,充当内存池的角色。请求队列中的请求项的内存从请求项数组中进行分配,请求操作完成后,请求项会被释放回该内存池中。
- 请求队列
处理读写设备请求时,从请求项数组中获取一个空闲块,建立请求项,并插入设备请求队列中。
块设备操作方式在内核与块设备进行数据交换(IO)时,涉及到三个对象之间的交互作用,其中主要需要了解系统和控制器之间的交互过程。
- 系统(内核):内核可以向控制器发送命令(发出请求)或等待设备控制器发出中断请求(完成请求)。
- 控制器:完成读写操作后,向系统(CPU)发出中断。
- 驱动器:受设备控制器控制
写数据过程
- 系统向控制器发出写命令后,等待块设备准备好接收数据,期间CPU需要主动查询控制器的状态寄存器判断是否准备好接收数据(忙等待)。
- 控制器准备好后,CPU将数据写入控制器的缓存中。(控制器具有一块缓存)
- 控制器将缓存中的数据通过驱动器全部写入块设备。
- 向CPU发出中断,中断处理程序完成写数据后的处理
读数据过程
- 向控制器发出读命令
- 控制器读取磁盘数据到其缓存中
- 向系统发起中断,中断处理程序从控制器缓存中取走数据
ll_rw_block函数将请求放入块设备请求队列,设备中断处理程序会不断处理请求队列,直到队列为空。
- 写请求的处理过程
-
ll_rw_block为写请求建立请求项,并插入设备的请求队列。若插入前队列为空,则调用请求项操作函数(request_fn)处理当前写请求,对块设备进行写操作。
-
设备块设备完成一个写请求后,向CPU发出中断信号,CPU运行中断处理函数。
-
中断处理程序判断是否还有数据要写(一个请求会可能写多个扇区),如果有则继续写,然后等待下一次中断,这一过程重复直到该写请求数据全部写入设备。
-
完成数据写之后,中断处理程序将唤醒一些等待进程
- 唤醒等待该请求项有关数据的相关进程
- 唤醒等待请求项的进程(当内存池没有空闲请求项时,将导致某些请求读写的进程睡眠)
- 释放当前请求项并从链表中删除该请求项以及释放锁定的相关缓冲区。
-
唤醒等待进程后,中断处理程序将调用请求项操作函数处理下一个读/写请求。
- 读请求处理过程
与写请求处理过程类似
- ll_rw_block与中断处理程序
ll_rw_block,负责将读写请求放入队列中,并启动对请求队列的处理。
中断处理程序,设备完成读写操作后,发出中断,不断处理请求队列中的请求操作,直到请求队列为空。
相关知识- I/O调度算法,读写请求不是按访问顺序放入请求队列的,0.11内核使用电梯算法调度读写请求
- 每个块设备具有一个请求队列,对块设备的读写请求被插入到队列中。
- 中断处理程序不断处理请求队列中的请求,直到队列为空。完成某个请求后,会唤醒一些等待的进程。