Linux块设备驱动框架分析
1、块设备简介
块设备,I/O设备的一种,其将信息存储在固定大小的块中,每一块都有固定的地址,可在设备的任意位置读取一定长度的数据。典型的有硬盘、U盘、SD卡等。
本文将以硬盘为例,对硬盘的结构属性进行简单介绍。
硬盘的物理结构主要由盘体、控制电路、接口部件等组成。而硬盘的内部结构一般是指盘体的内部结构。盘体是一个密封的交替,里面包含磁头、盘片等部件。硬盘驱动器采用高精度、轻型磁头驱动/定位系统。这种系统磁头可以在盘面上快速移动。硬盘的数据管理是基于逻辑结构的。
硬盘的逻辑结构如图所示,由盘面、磁道、柱面以及扇区构成。
盘面号:硬盘的盘片一般有两个盘面,分为上、下盘面,个别硬盘盘面为单数。每一个盘面按照从上至下的顺序进行编号,得到盘面号(也叫磁头号)。每一个盘面对应一个读写磁头。
磁道:硬盘在格式化时,会被划分成很多同心圆,这些同心圆的轨迹成为磁道。
柱面:所有盘面的同一个磁道构成一个圆柱,称为柱面。硬盘数据的读写按照从上往下并按照柱面进行。同一个柱面的磁头按照从上到下的顺序全部读完后,才会切换到下一个柱面。磁头之间的切换是通过电子切换,而柱面的切换则是通过机械实现的。由于电子切换速度比机械切换的速度快,所以按照柱面读写,而不是按照盘面来读写。
扇区:磁道被划分为一段段圆弧,称之为扇区。外圈磁道的圆弧的线速度比内圈磁道圆弧的线速度高。硬盘按照扇区的形式进行信息的存储。
硬盘读写数据的过程:现代硬盘采用CHS的方式寻道。读写磁头在径向方向移动到目标扇区所在磁道的上方的时间称为寻道时间。磁头到达目标位置,通过盘片旋转,将要读取的扇区转到磁头下面的时间称为旋转延时时间。
硬盘容量的计算:硬盘容量=盘面数*柱面数*扇区数*512字节
因为硬盘以扇区为单元进行读写,为了提高块设备的读写速度,先将需要读写的数据放入队列,优化读写的顺序后,在执行读写操作,保证对同一个扇区的读写操作只执行一次,提高效率。
2、块设备驱动框架
App: open, read, write, 文件的读写
文件系统:vfat, ext2, ext3,yaffs2 将对文件的读写转换成对扇区的操作
ll_rw_block
1、把"读写"放入队列
2、调用队列的处理函数(优化/调顺序/合并)
块设备驱动程序
硬件: 硬盘、flash
分析ll_rw_block函数如下。
void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
for (i = 0; i < nr; i++){
struct buffer_head *bh = bhs[i];
submit_bh(rw, bh);
struct bio *bio;//bio:block input output
//使用bh构建bio
submit_bio(rw, bio);//提交bio
generic_make_request(bio);//使用bio构造request请求
__generic_make_request(bio);
request_queue_t *q, q = bdev_get_queue(bio->bi_bdev);//获取块设备请求队列
ret = q->make_request_fn(q, bio);//调用队列的构造请求函数
//默认请求函数是__make_request
__make_request
//先尝试将bio合并到q队列中
el_ret = elv_merge(q, &req, bio);
//若合并不成功,则通过bio构建请求
init_request_from_bio(req, bio);
add_request(q, req);//将请求添加到队列
__generic_unplug_device(q);//执行队列的请求
q->request_fn(q);//调用队列的请求函数
}
块设备驱动程序框架:
1、 分配gendisk: alloc_disk
2、 设置
2.1 分配/设置队列:request_queue_t //提供读写能力
blk_init_queue
2.2设置gendisk其他信息
3、 注册:add_disk