整个块设备抽象的数据结构
struct sbull_dev {
int size; /* Device size in sectors */
u8 *data; /* The data array */
short users; /* How many users */
short media_change; /* Flag a media change? */
spinlock_t lock; /* For mutual exclusion */
struct request_queue *queue; /* The device request queue */请求队列
struct gendisk *gd; /* The gendisk structure */ /块设备数据结构
struct timer_list timer; /* For simulated media changes */
};
//模块初始化函数
static int __init sbull_init(void)
{
int i;
/*
* Get registered.
*/
sbull_major = register_blkdev(sbull_major, "sbull");//自动分配主设备号
if (sbull_major <= 0) {
printk(KERN_WARNING "sbull: unable to get major number/n");
return -EBUSY;
}
/*
* Allocate the device array, and initialize each one.
*/
Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);//分配每个块设备的数据结构
if (Devices == NULL)
goto out_unregister;
for (i = 0; i < ndevices; i++)
setup_device(Devices + i, i);
return 0;
out_unregister:
unregister_blkdev(sbull_major, "sbd");
return -ENOMEM;
}
static void setup_device(struct sbull_dev *dev, int which)
{
/*
* Get some memory.
*/
memset (dev, 0, sizeof (struct sbull_dev));
dev->size = nsectors*hardsect_size;
dev->data = vmalloc(dev->size);
if (dev->data == NULL) {
printk (KERN_NOTICE "vmalloc failure./n");
return;
}
spin_lock_init(&dev->lock);
/*
* The timer which "invalidates" the device.
*/
init_timer(&dev->timer);
dev->timer.data = (unsigned long) dev;
dev->timer.function = sbull_invalidate;//初始化超时定时器
/*
* The I/O queue, depending on whether we are using our own
* make_request function or not.
*/
switch (request_mode) {
case RM_NOQUEUE:
dev->queue = blk_alloc_queue(GFP_KERNEL);
if (dev->queue == NULL)
goto out_vfree;
blk_queue_make_request(dev->queue, sbull_make_request);
break;
case RM_FULL:
dev->queue = blk_init_queue(sbull_full_request, &dev->lock);
if (dev->queue == NULL)
goto out_vfree;
break;
default:
printk(KERN_NOTICE "Bad request mode %d, using simple/n", request_mode);
/* fall into.. */
case RM_SIMPLE:
dev->queue = blk_init_queue(sbull_request, &dev->lock);//向系统动态申请请求队列
if (dev->queue == NULL) //sbull_request为个人实习的请求队列处理函数
goto out_vfree;
break;
}
blk_queue_hardsect_size(dev->queue, hardsect_size);
dev->queue->queuedata = dev;
/*
* And the gendisk structure.
*/
dev->gd = alloc_disk(SBULL_MINORS);//申请块设备数据结构
if (! dev->gd) {
printk (KERN_NOTICE "alloc_disk failure/n");
goto out_vfree;
}
dev->gd->major = sbull_major;
dev->gd->first_minor = which*SBULL_MINORS;
dev->gd->fops = &sbull_ops;
dev->gd->queue = dev->queue;//将请求队列与块设备绑定
dev->gd->private_data = dev;
snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));
add_disk(dev->gd);//最后添加到系统
return;
out_vfree:
if (dev->data)
vfree(dev->data);
}
static void sbull_exit(void)
{
int i;
for (i = 0; i < ndevices; i++) {
struct sbull_dev *dev = Devices + i;
del_timer_sync(&dev->timer);
if (dev->gd) {
del_gendisk(dev->gd);
put_disk(dev->gd);
}
if (dev->queue) {
if (request_mode == RM_NOQUEUE)
blk_put_queue(dev->queue);
else
blk_cleanup_queue(dev->queue);//返回请求队列
}
if (dev->data)
vfree(dev->data);
}
unregister_blkdev(sbull_major, "sbull");//返回设备号
kfree(Devices);
}
最简单的处理请求队列模型
static void sbull_request(request_queue_t *q)
{
struct request *req;
while ((req = elv_next_request(q)) != NULL) {//elv_next_request获得请求队列中的下一个请求
struct sbull_dev *dev = req->rq_disk->private_data;
if (! blk_fs_request(req)) {
printk (KERN_NOTICE "Skip non-fs request/n");
end_request(req, 0);
continue;
}
// printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx/n",
// dev - Devices, rq_data_dir(req),
// req->sector, req->current_nr_sectors,
// req->flags);
//req->sector 要传输的下一个缓冲区
//req->current_nr_sectors 当前要传输的扇区数目
//req->buffer 数据应当从这个缓冲区来或者去
//rq_data_dir 可获得这个请求的数据传送方向
sbull_transfer(dev, req->sector, req->current_nr_sectors,
req->buffer, rq_data_dir(req));
end_request(req, 1);
}
}
//传输数据
static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,
unsigned long nsect, char *buffer, int write)
{
//此次传输的偏移量
unsigned long offset = sector*KERNEL_SECTOR_SIZE; //req->sector 要传输的下一个扇区
unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;//nsect = req->current_nr_sectors 当前要传输的扇区数目
//nbytes 此次要传输的字节数目
if ((offset + nbytes) > dev->size) {
printk (KERN_NOTICE "Beyond-end write (%ld %ld)/n", offset, nbytes);
return;
}
if (write)
memcpy(dev->data + offset, buffer, nbytes);
else
memcpy(buffer, dev->data + offset, nbytes);
}
每一个块设备的IO请求数据都是通过bio结构体表示,每个请求包含一个或者多个块(即是struct bio 中的数组)
块设备IO请求由结构体 struct request表示,因为一个请求可能包含多个连续磁盘块,所以可能包含多个struct bio结构体。
多个struct request 再链接成 struct request_queue。request_queue再与每个磁盘的gendisk捆绑在一起。
struct bio{
struct bio_vec *bi_io_vec;//指向数组的首地址
unsigned short bi_idx;//数组的标号
unsigned short bi_vcnt;//总共的片段数量
.......
}
struct bio_vec{//一个数据结构代表一个缓存片段
struct page *bv_page;//被缓存的内存页
unsigned int bv_len;//数据长度
unsigned int bv_offset;//页内偏移
}