一,Linux块设备子系统:
图一,来自深入理解Linux内核架构
图二,是本人通过阅读内核源代码总结的分层结构:
Linux块设备驱动程序的主要任务:
1,注册设备(获取主设备号)
2,块设备驱动上层通路:
调用IO子系统提供的接口分配并初始化请求队列,绑定队列请求处理函数;
设置请求能够处理的最大硬件扇区数;
设置硬件数据块大小;
3,块设备驱动底层通路:
分配块设备结构实例;
初始化块设备结构实例(名字,主设备号,第一个次设备号,块设备操作,设备容量——以512字节为单位的扇区数量);
添加块设备结构实例到内核;
二,Linux块设备驱动程序实现:
1,设备相关数据结构:
<linux/genhd.h>
struct gendisk{
int major;
int first_minor;
int minors;
char disk_name[DISK_NAME_LEN];
struct block_device_operations ops;
void *private_data;
struct request_queue *queue;
... ...
};
<linux/fs.h>
struct block_device_operations {
int (*open)(struct block_device *dev,fmode_t mode);
void (*release)(struct gendisk *gdisk);
int (*rw_page)(struct gendisk *gdisk,fmode_t mode);
... ...
};
2,读写操作请求相关的数据结构:
<linux/bio.h>
struct bio {//设备扇区与缓存的内存段的映射管理;
struct bio *bi_next;
struct bvec_iter bi_iter; //设备扇区索引;
struct bio_vec *bi_io_vec; //扇区数据对应的缓存段;
... ...
};
<linux/blkdev.h>
struct request {
struct bio *bio;
... ...
};
上述请求相关数据结构之间的关系如下图:
数据结构相关的操作api函数:
注册设备: int register_blkdev(int major,char *name);//如果major为0则动态分配并返回主设备号;
请求队列处理函数: void (*request_fn_proc)(struct request_queue *queue);
请求获取函数: struct request *blk_peek_request(struct request_queue *queue);
从请求队列删除请求: void blk_start_request(struct request *request);
结束请求: void blk_end_request_all(struct request *request);// void __blk_end_request_all(struct request *request);
请求队列分配并初始化: struct request_queue *blk_init_queue(void (*request)(struct request_queue *queue));
遍历请求的bio: rq_for_each_bio(bio,rq);
遍历bio的每个段: bio_for_each_segment(bvec,bio,iter);
获取数据船速方向: rq_data_dir(rq);
获取bio当前段的字节数: bio_cur_bytes(bio,iter);
释放请求队列: blk_cleanup_queue(struct request_queue *queue);
分配设备: struct gendisk *alloc_disk(int minors);
设置设备capacity: void set_capacity(struct gendisk *disk,sector_t size);//size是以512为单位的扇区数量;
添加设备: void add_disk(struct gendisk *disk);
删除设备: void del_disk(struct gendisk *disk);
3,块设备驱动示例主要逻辑代码(simple_ramdisk):
1,自定义块设备数据结构:
struct simple_ramdisk {
int size;
u8 *data;
short user;
spinlock_t spin_lock;
struct gendisk *disk;
struct request_queue *queue;
};
struct simple_ramdisk *sramdisk;
1,设备初始化:
int setup_simple_ramdisk(){
int major=register_blkdev(0,"simple_ramdisk");
sramdisk=kmalloc(sizeof(struct simple_ramdisk),GFP_KERNEL);
memset(sramdisk,0,sizeof(struct simple_ramdisk));
sramdisk->size=SECTORS*SECTOR_SIZE;
sramdisk->data=vmalloc(SECTORS*SECTOR_SIZE);
memset(sramdisk->data,0,SECTORS*SECTOR_SIZE);
if(!sramdisk->data)
return -ENOMEM;
spin_lock_init(&sramdisk->spin_lock);
/* 1,上行通道; */
sramdisk->queue=blk_init_queue(request_fn_proc , &spin_lock);
if(!sramdisk->queue)
return -ENODEV;
blk_queue_physical_block_size(sramdisk->queue,SECTOR_SIZE);
blk_queue_max_hw_sectors(sramdisk->queue,1024);
/* 2,底层通道; */
sramdisk->disk=alloc_disk(1);//此处一定要是1,否则在insmod模块的时候需要check_partions就会挂起;
sramdisk->disk->major=major;
sramdisk->disk->first_minor=0;
sramdisk->disk->ops=block_ops;
sramdisk->disk->queue=sramdisk->queue;
sramdisk->disk->private_data=sramdisk;
snprintf(sramdisk->disk->disk_name,DISK_NAME_LEN,"simple_ramdisk%c",'a');
set_capacity(sramdisk->disk,SECTORS*SECTOR_SIZE/512);
add_disk(sramdisk->disk);
}
2,请求处理函数:
void transfer(struct simple_ramdisk *rd,sector_t sector,int sectors,u8 *buffer,bool iswrite){
unsigned long offset=sector_t<<9;
unsigned long len=sectors<<9;
if(offset+len>rd->size)
return;
if(iswrite){//向块设备写入数据;
memcpy(rd->data+offset,buffer,len);
} else {//从块设备读取数据到页缓存;
memcpy(buffer,rd->data+offset,len);
}
}
void request_fn_proc(struct request_queue *queue){
struct bio *bio;
struct bio_vec *b_vec;
struct bvec_iter bi_iter;
struct request *rq;
sector_t sector;
struct simple_ramdisk *sramdisk;
while((rq=blk_queue_peek_request())!=NULL){
sramdisk=rq->rq_disk->private_data;
if(! blk_account_rq(rq)){
blk_start_request(rq);
blk_end_request_all(rq,-EIO);
continue;
}
blk_start_request(rq);
rq_for_each_bio(bio,rq){
sector=bio->bi_iter.sector
bio_for_each_segment(bvec,bio,bi_iter){
u8 *buffer=__bio_kmap_atomic(bio,iter);
transfer(sramdisk,sector,bio_cur_bytes(bio)>>9,buffer,rq_data_dir(rq)==WRITE);
sector+=bio_cur_bytes(bio)>>9;
__bio_kunmap_atomic(buffer);
}
}
}
}
3,块设备操作相关函数:
int simple_ramdisk_open(struct block_device *device,fmode_t mode){
struct simple_ramdisk *srd=device->bd_disk->private_data;
spin_lock_irqsave(flags);
srd->user++;
spin_unlock_irqrestore(flags);
return 0;
}
void simple_ramdisk_release(struct gendisk *disk,fmode_t mode){
struct simple_ramdisk *srd=device->bd_disk->private_data;
spin_lock_irqsave(flags);
srd->user--;
spin_unlock_irqrestore(flags);
}
int simple_ramdisk_rw_page(struct block_device *device , sector_t sector , struct page *page , bool iswrite){
return endpage(page);
}
struct block_device_operations block_ops={
.open=simple_ramdisk_open,
.release=simple_ramdisk_release,
.rw_page=simple_ramdisk_rw_page
};