块设备驱动程序
<1>.块设备和字符设备的区别
1,读取数据的单元不同,块设备读写数据的基本单元式块,字符设备的基本单元是字节。
2,块设备可以随机访问,字符设备只能顺序访问。
<2>.linux内核中块设备的描述
struct
gendisk {
int major;主设备号
int first_minor;次设备号
int
minors;
char disk_name[DISK_NAME_LEN];驱动名
struct
block_device_operations *fops;
struct request_queue *queue;
请求队列
………………………………………………
int node_id;
};
设备操作
struct
block_device_operations {
int (*open)(struct block_device *,fmode_t
);
int (*release)(struct gendisk * ,fmode_t);
int (*ioctl)(struct
block_device *,fmode_t ,unsigned ,unsigned
long);
………………………………………………………………………………
}
<3>.设备注册
void add_disk(struct gendisk
*gd);
<4>.IO请求
linux内核中,使用struct request来表示等待处理的块设备IO请求。
struct
request
{
struct list_head queuelist;链表结构
sector_t
sector;要操作的首个扇区
unsigned long nr_sectors;要操作的扇区个数
struct bio *bio;请求的bio结构体的链表
struct bio
*biotail;请求的bio结构体的链表尾
…………………………………………………………
}
<5>.请求队列
请求队列就是IO请求request所形成的队列。在linux内核中用struct
request_queue来描述。
<6>.队列操作函数
struct request_queue
*blk_init_queue(request_fn_proc *rfn,spinlock_t
*lock);
初始化请求队列,一般在模块加载函数中被调用。
void blk_cleanup_queue(request_queue
*q);
清除请求队列,完成将请求队列返回给系统任务,一般在模块卸载函数中调用。
struct request
*elv_next_request(request_queue_t
*queue);
返回下一个要处理的请求,如果没有请求则返回NULL。elv_next_request()不会清除请求,仍然把这个请求放到
队列上,因此,连续调用它两次,会返回同一个请求结构体。
void
blkdev_dequeue_request(struct request
*req);从队列中删除一个请求结构体。
<7>.块设备驱动测试
insmod simple-blk.ko
ls
/dev/simp_blkdev
mkfs.ext3 /dev/simp_blkdev
mkdir -p
/mnt/blk
mount
cp /etc/init.d/* /mnt/blk
ls /mnt/blk
umount
/mnt/blk
ls /mnt/blk
<8>.bio结构
一个struct bio代表一次块设备的IO请求,IO调度器可将连续的bio合并成一个请求struct
request。
struct bio
{
sector_t bi_sector; 第一个要访问的扇区
unsigned
int bi_size;以字节为单位要传输的数据大小
struct bio_vec
*bi_io_vec;实际的vector列表
}
struct bio_vec
{
struct page
*bv_page;页指针
unsigned int bv_len;要传输数据的长度
unsigned int
bv_offset;偏移量
}
在_make_request函数中,使用了IO调度器,将多个bio访问顺序进行优化,调整,合并成一个request,然后提交给用户
指定的处理函数。然而对于U盘,ramdisk,记忆棒之类的设备,并不存在磁盘所需要的寻道时间。因此对这样的设备而言
一个IO调度器,不但发挥不了作用,而且会浪费很多的内存和CPU资源。
解决方法:驱动程序自己实现request_queue所需要的make_request_fn
函数,不使用_make_request函数。
<9>.请求处理的另一种实现方法。
分配请求队列。
request_queue_t
*blk_alloc_queue(int
fgp_mask);
对于FLASH,RAM等完全随机访问的非机械设备,并不需要进行复杂的IO调度,这个时候应该使用上述函数分配一个请求队列。
void
blk_queue_make_request(request_queue_t *q,make_request_fn
*fn);
绑定请求队列和制造请求函数