概述
与字符设备不同,块设备一开始加载就已经打开。用户对他的open操作只是在已经打开的设备上
找一个索引节点(即找到文件存储的位置)。不用写自动生成设备文件的人相关函数调用,他天生
会自动生成设备文件,生成的设备文件保存在/dev/block/ 目录下。
1.竞态:
几个进程同时读写时会不会有冲突呢?大文件拷贝的过程中又有一个小文件需要拷贝时,
是先等大文件拷贝完在开始小文件的拷贝吗?要是大文件有1T要拷贝1个小时,是否还是等其拷贝
完成再启动下个文件的拷贝?
解决以上问题,内核自有一套调度算法,通过各种调度策略(如电梯算法)将块设备的读写任务
规划成合理的的任务块并将这些任务块排成一个队列,我们称其为“请求队列”。
对于驱动开发者,无需关心这些调度算法,我们只要从请求请队列中取“请求”就行了,
描述传输属性的bio_vec,向上封装成bio,再经过规划变成request,再经过调度算法把多个请
求排序变成request_queue.
函数接口
1.出处与定义
include/linux/genhd.h
struct gendisk {
int major;
int first_minor;
int minors;
char disk_name[DISK_NAME_LEN];
struct disk_part_tbl __rcu *part_tbl;
struct hd_struct part0;
const struct block_device_operations *fops;
struct request_queue *queue;
void *private_data;
...
};
2.设备号:
int register_blkdev(unsigned int, const char *);
void unregister_blkdev(unsigned int, const char *);
3.容量:
/*
* 功能:得到磁盘容量
* 输入参数:struct gendisk *disk:磁盘对象
* 返回值:磁盘总扇区数
*/
sector_t get_capacity(struct gendisk *disk)
/*
* 功能:设置磁盘容量
* 输入参数:struct gendisk *disk:磁盘对象
* sector_t size; 扇区数
* 返回值:none
*/
void set_capacity(struct gendisk *disk, sector_t size)
4.操作方法集:
struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
void (*release) (struct gendisk *, fmode_t);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*media_changed) (struct gendisk *);
int (*revalidate_disk) (struct gendisk *);
int (*getgeo)(struct block_device *, struct hd_geometry *);
struct module *owner;
...
};
5.请求队列:
<include/linux/blkdev.h>
struct request_queue {
request_fn_proc *request_fn; //
make_request_fn *make_request_fn;
...
}
typedef void (request_fn_proc) (struct request_queue *q);
typedef void (make_request_fn) (struct request_queue *q, struct bio *bio);
<include/linux/bio.h>
struct bio {
//块传输控制的各种属性
...
}
没有经过I/O调度的包
struct bio {
unsigned long bi_flags; /* status, command, etc */
unsigned long bi_rw; /* 方向bottom bits READ/WRITE,
struct bvec_iter bi_iter; //外存首地址
unsigned short bi_max_vecs;//最大的内存块
atomic_t bi_cnt; /*当前内存块 pin count */
struct bio_vec *bi_io_vec; //内存块
...
};
<include/linux/bvec.h>
struct bio_vec {
struct page *bv_page;
unsigned int bv_len;
unsigned int bv_offset;
};
struct request {
u64 cmd_flags;
unsigned int __data_len;
sector_t __sector;
struct bio *bio;
int errors;
...
};
请求的操作函数
/*
* 功能:创建并初始化请求队列,用于需要调度的机械硬盘等设备
* 输入参数:funcp: 处理请求队列的回调函数
* spin_lock: 自旋锁
* 返回值:none
*/
void blk_init_queue(funcp,spin_lock);
/*
* 功能:创建伪请求队列,用于不需要调度的固态硬盘等设备
* 输入参数:flg: 权限,一般用GFP_KERNEL
* 返回值:none
*/
void blk_alloc_queue(flg);
/*
* 功能:给伪请求队列绑定处理函数,用于不需要调度的固态硬盘等设备
* 输入参数:rq_queue: 权限,一般用GFP_KERNEL
* rq_queue: 处理伪请求队列的回调函数
* 返回值:none
*/
void blk_queue_make_request(rq_queue, func);
·
void blk_cleanup_queue(struct request_queue *q)
/*
* 功能:从请求队列中提取出一个请求
* 输入参数:struct request_queue *q:请求队列
* 返回值:得到的请求
*/
struct request *blk_fetch_request(struct request_queue *q);
sector_t blk_rq_pos(const struct request *rq)
int blk_rq_cur_bytes(const struct request *rq)
unsigned int blk_rq_cur_sectors(const struct request *rq)
int rq_data_dir(struct request *rq)
void *bio_data(struct bio *bio);
gendisk相关的操作函数
struct gendisk *alloc_disk(int max_secter)
void put_disk(struct gendisk *disk)
add_disk();
add_disk();
范例
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
#include <asm/current.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
static struct gendisk *blkdev = NULL;
static spinlock_t lock;
#define BLKNAME "blkdev"
#define SECTOR 512
#define SECTNR (1024*2*10)
static char *addrp = NULL;
static int blkdev_open(struct block_device *dev, fmode_t mode)
{
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
return 0;
}
static int blkdev_release(struct gendisk *genhd, fmode_t mode)
{
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
return 0;
}
static int blkdev_getgeo(struct block_device *blkdev, struct hd_geometry *geo)
{
unsigned long size;
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
size = SECTNR*SECTOR;
geo->cylinders = (size & ~0x3f) >> 6;
geo->heads = 4;
geo->sectors = 16;
geo->start = 0;
return 0;
}
static void rw_proc(struct request_queue *q)
{
struct request *rq = NULL;
unsigned long offset;
int len;
char *buffer = NULL;
while(1){
if(!rq){
rq = blk_fetch_request(q);
if(NULL == rq)
break;
}
if (rq->cmd_type != REQ_TYPE_FS){
printk (KERN_ERR "Skip non-fs request\n");
__blk_end_request_all(rq, -EIO);
break;
}
offset = blk_rq_pos(rq) << 9;
len = blk_rq_cur_bytes(rq);
buffer = bio_data(rq->bio);
if((offset+len) > SECTNR*SECTOR){
printk (KERN_ERR "no space\n");
__blk_end_request_all(rq, -EIO);
break;
}
if(WRITE == rq_data_dir(rq)){
memcpy(addrp+offset, buffer, len);
}else{
memcpy(buffer, addrp+offset, len);
}
if(!__blk_end_request_cur(rq, 0)){
rq = NULL;
}
}
}
static struct block_device_operations ops = {
.owner = THIS_MODULE,
.open = blkdev_open,
.release= blkdev_release,
.getgeo = blkdev_getgeo,
};
static int __init blkdev_init(void)
{
int ret;
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - entry\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
blkdev = alloc_disk(3);
if(NULL == blkdev){
return -ENOMEM;
}
blkdev->major = register_blkdev(0, BLKNAME);
if(0 > blkdev->major){
ret = blkdev->major;
goto ERR_STEP;
}
blkdev->first_minor = 0;
snprintf(blkdev->disk_name, DISK_NAME_LEN, "%sa", BLKNAME);
set_capacity(blkdev, SECTNR);
blkdev->fops = &ops;
spin_lock_init(&lock);
blkdev->queue = blk_init_queue(rw_proc, &lock);
if(!blkdev->queue){
ret = -ENOMEM;
goto ERR_STEP1;
}
blk_queue_logical_block_size(blkdev->queue, SECTOR);
addrp = vmalloc(SECTOR*SECTNR);
if(!addrp){
ret = -ENOMEM;
goto ERR_STEP1;
}
add_disk(blkdev);
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - ok\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
return 0;
ERR_STEP1:
unregister_blkdev(blkdev->major, BLKNAME);
ERR_STEP:
put_disk(blkdev);
return ret;
}
static void __exit blkdev_exit(void)
{
unregister_blkdev(blkdev->major, BLKNAME);
blk_cleanup_queue(blkdev->queue);
put_disk(blkdev);
vfree(addrp);
del_gendisk(blkdev);
printk(KERN_INFO "(%s:pid=%d), %s : %s : %d - leave\n",
current->comm, current->pid, __FILE__, __func__, __LINE__);
}
module_init(blkdev_init);
module_exit(blkdev_exit);
MODULE_LICENSE("GPL");
实例二:
