块设备驱动分析 | |
| |
块设备驱动分析,基于sbull 开始之前先来了解这个块设备中的核心数据结构: 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 */ }; 在这个结构中,struct request_queue与 struct gendisk 是这个结构中的重要成员, 也是块设备中的重要结构,这个文章中的大部分阐述都是基于这两个结构的操作。 一.流程: 块设备驱动也是从init函数开始的,所以分析也从这里开始。 第一步: register_blkdev(sbull_major, "sbull"); 先注册块设备,第一个参数是设备号,为0表完动态分配,第二个为设备名。 第二步: Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL); 创建这个块设备的核心数据结构,也就是这个块设备的对象实体,创建了ndevice这样 的实体。 第三步: setup_device(Devices + i, i); 说白了,就是初始化实体,并把它添加到系统的block层中去。这个步骤很重要,它完 成以以下一些操作: 1.初始化一个自旋锁。 spin_lock_init(&dev->lock); 2.分配一个请求队列,并用1中的自旋锁来控制对队列的访问。 dev->queue = blk_init_queue(sbull_full_request, &dev->lock); 3.分配,初始化及安装相应的gendisk结构。 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)); 4.最后add_disk完成整个初始化过程,这步一定要在初始化的最后再调用,因为 add_disk后,可能就会调用磁盘的操作函数,如果初始化还没有完成就会出错。 二.块设备操作 struct block_device_operations 结构分析: sbull模块中的该结构: static struct block_device_operations sbull_ops = { .owner = THIS_MODULE, .open = sbull_open, .release = sbull_release, .media_changed = sbull_media_changed, .revalidate_disk = sbull_revalidate, .ioctl = sbull_ioctl }; open与release这两个函数就不再具体分析,它们有一个重要功能就是增加用户计数和 减少用户计数。media_changed 和revalidata_disk即是对可移动介质的支持,像u盘等 等这些可移动,即插即用的设备就应该实现这两个函数。media_changed是检查介质是 否改变,发迹即返回非零,revalidate_disk即介质改变后执行。它们之间如何联系我 们不用管,我们主要实现这个函数的实体即可。 ioctl函数,ioctl函数的功能也简化了,可实际的磁盘设备大多也主要是实现对磁盘信 息的查询。 三.请求处理。 块设备驱动程序的核心是请求处理部分,是块设备驱动的难点。设计得好否直接关 系到设备的性能。 我们看在安装块设备实体的时候,初始化了一个请求队列: dev->queue = blk_init_queue(sbull_full_request, &dev->lock); 这个操作就是把生成的请求队列dev->queue与请求函数sbull_full_request绑定在一 起。sbull中的request函数: static void sbull_full_request(request_queue_t *q) { struct request *req; int sectors_xferred; struct sbull_dev *dev = q->queuedata; while ((req = elv_next_request(q)) != NULL) { if (! blk_fs_request(req)) { printk (KERN_NOTICE "Skip non-fs request/n"); end_request(req, 0); continue; } sectors_xferred = sbull_xfer_request(dev, req); if (! end_that_request_first(req, 1, sectors_xferred)) { blkdev_dequeue_request(req); end_that_request_last(req); } } } req = elv_next_request(q) 获取队列中第一个未完成的请求,两次调用而没有 运行end_that_request_last或者end_request时得到的是相同的结果,因为它不会删除 队列中的请求。只有结束该请求,才会得到下一个请求。sbull_xfer_request在这里即 是实际的数据传输。 一个实际的块设备请求处理要这些要复杂得多,那就要更深入了解request结构,bio结 构,队列结构等等。但在这里先不深入去讨论。 |
块设备驱动分析
最新推荐文章于 2024-05-15 13:15:00 发布