《unix设备驱动》块设备驱动程序(加示例代码)

块设备驱动程序

一个块设备驱动程序主要通过传输固定大小的随机数据来访问设备。

块驱动程序是在核心内存和其他存储介质之间的管道,因此他们可以认为是虚拟内存子系统的组成部分。

 

一些概念

一个数据块指的是固定大小的数据,而大小的值有内核决定

 与数据块对应的是扇区,它是由底层硬件决定大小的一个块。

 无论何时内核向用户提供一个扇区编号,该扇区的大小就是512字节。

注册

注册的目的:使内核知道设备的存在

注册块设备驱动程序

注册到内核


int register_blkdev(unsigned int major, const char *name); 

参数为主设备号和设备名字

 

注销函数:


int unregister_blkdev(unsigned int major, const char *name); 

2.6中register_blkdev所做的事情:

如果需要的话分配一个动态的主设备号。

在/proc/devices 中创建一个入口项。

注册磁盘

虽然register_blkdev能获得主设备号,但是它并不能让系统使用任何磁盘

块设备操作

 block_device_operations结构告诉系统对他们的操作接口。


int (*open)(struct inode *inode, struct file *filp); 

int (*release)(struct inode *inode, struct file *filp); 

int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); 

实现ioctl系统函数的调用。


int (*media_changed) (struct gendisk *gd); 

检查用户是否更换了驱动器介质

该函数只适合于移动介质


int (*revalidate_disk) (struct gendisk *gd); 

当介质变更时该函数做出响应,告诉驱动程序完成响应的工作,以便使用新的介质


struct module *owner; 

指向该结构体的模块指针。

 

这里面没有函数负责数据的读写,在块设备中,这些操作是由request函数处理的。

Gendisk结构

内核使用gendisk结构表示一个独立的磁盘设备。

Gendisk结构的许多成员需要由驱动程序初始化。


int major; 

int first_minor; 

int minors; 

一个驱动器至少使用一个次设备号,如果驱动器是可分区的那么用户需要为每个可能的分区都分配一个次设备号。Minors常取16

char disk_name[32];

设置磁盘驱动器的名字.


struct block_device_operations *fops; 

设置前一节描述的各种设备操作


struct request_queue *queue; 

内核使用该结构为设备管理IO请求


int flags; 

用来描述驱动器状态的标志.

设备为可移动介质: GENHD_FL_REMOVABLE.

 CD-ROM 驱动器:  GENHD_FL_CD.

不需要分区信息出现在 /proc/partitions:

GENHD_FL_SUPPRESS_PARTITIONS_INFO.


sector_t capacity; 

以512字节为一个扇区时,该函数可以包含扇区数. sector_t可以是 64 位宽. 驱动不能直接设置这个成员,而要将扇区数传递给set_capacity.

void *private_data;

块驱动可使用这个成员作为一个指向其内部的指针.

 

内核为Gendisk结构提供的一些函数:

struct gendisk 是一个动态分配的结构, 它需要特殊的内核操作来初始化; 驱动不能自己分配这个结构. 而必须调用:


struct gendisk *alloc_disk(int minors); 

 

minors 参数表示次设备号,此后用户不能改变该成员。

不需要一个磁盘时,调用下面的函数卸载磁盘:


void del_gendisk(struct gendisk *gd);

 gendisk 是一个引用计数的结构(它含有一个 kobject).  get_disk 和 put_disk 负责引用计数, 但是驱动不能直接使用这两个函数.

分配一个 gendisk 结构不能使系统可使用这个磁盘. 要做到这点, 必须初始化这个结构并且调用 add_disk:


void add_disk(struct gendisk *gd); 

驱动程序完全初始化并能相应对磁盘的请求前请不要调用add_disk.

 

下面这几点可以结合最后面的示例代码(模拟块设备程序)来理解,我在里面添加了很详细的注释

Sbull的初始化

对扇区大小的说明

块设备操作

Open和release函数

对可移动介质的支持

Ioctl函数

请求处理

Request函数简介

一个简单的request函数

请求队列

请求过程

本质上 ,一个request结果是作为一个bio结构的链表实现的

 

Bio结构是在底层对部分设备IO请求的描述


sector_t bi_sector; 

这个 bio 要被传送的第一个(512字节)扇区.


unsigned int bi_size; 

被传送的数据大小, 以字节计. 相反, 常常更易使用 bio_sectors(bio), 一个给定以扇区计的大小的宏.


unsigned long bi_flags; 

一组描述 bio 的标志;


unsigned short bio_phys_segments; 

unsigned short bio_hw_segments; 

包含在这个 BIO 中的物理段的数目, 和在 DMA 映射完成后被硬件看到的段数目,

一个 bio 的核心, 是一个称为 bi_io_vec 的数组:


struct bio_vec {

 struct page  *bv_page; 

 unsigned int  bv_len; 

 unsigned int  bv_offset;  

};  

 bio 

 

图 0‑1bio结构

 

为了能追踪正在处理的请求的当前状态,块设备也在bio结构中维护了一系列指针。提供了一些宏来访问这个状态。


struct page *bio_page(struct bio *bio); 

返回一个指向页结构的指针, 表示下一个被传送的页.


int bio_offset(struct bio *bio); 

返回页内的被传送的数据的偏移.


int bio_cur_sectors(struct bio *bio); 

返回要被传送出当前页的扇区数.


char *bio_data(struct bio *bio); 

返回一个内核逻辑地址, 指向被传送的数据. 


char *bio_kmap_irq(struct bio *bio, unsigned long *flags); 

void bio_kunmap_irq(char *buffer, unsigned long *flags); 

这个函数可以为任何缓冲区返回内核虚拟地址。
屏蔽请求

void blk_queue_ordered(request_queue_t *queue, int flag);

指出驱动程序要实现屏蔽的请求

 

屏蔽请求只是检查request结构中的相关标志,为此内核提供了一个宏:


int blk_barrier_rq(struct request *req); 

不可重试的请求

如果驱动程序要重试一个失败的请求,调用:


int blk_noretry_request(struct request *req); 

如果返回非0 ,驱动程序忽略这个请求并返回错误值,不在重试。

请求完成函数

当设备完成一个IO请求的部分或者全部扇区时,必须调用下面的函数通知块设备子系统:


int end_that_request_first(struct request *req, int success, int count);  
void end_that_request_last(struct request *req); 


 1 
 2  /*
 3  * sbull.h -- definitions for the char module
 4  *
 5  * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 6  * Copyright (C) 2001 O'Reilly & Associates
 7  *
 8  * The source code in this file can be freely used, adapted,
 9  * and redistributed in source or binary form, so long as an
10  * acknowledgment appears in derived source files.  The citation
11  * should list that the code comes from the book "Linux Device
12  * Drivers" by Alessandro Rubini and Jonathan Corbet, published
13  * by O'Reilly & Associates.   No warranty is attached;
14  * we cannot take responsibility for errors or fitness for use.
15  *
16  */
17 
18 
19 #include <linux/ioctl.h>
20 
21  /* Multiqueue only works on 2.4 */
22 #ifdef SBULL_MULTIQUEUE
23 #    warning "Multiqueue only works on 2.4 kernels"
24  #endif
25 
26  /*
27  * Macros to help debugging
28  */
29 
30  #undef PDEBUG             /* undef it, just in case */
31 #ifdef SBULL_DEBUG
32 #  ifdef __KERNEL__
33      /* This one if debugging is on, and kernel space */
34 #    define PDEBUG(fmt, args...) printk( KERN_DEBUG "sbull: " fmt, ## args)
35 #  else
36      /* This one for user space */
37 #    define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args)
38 #  endif
39 #else
40 #  define PDEBUG(fmt, args...) /* not debugging: nothing */
41 #endif
42 
43 #undef PDEBUGG
44 #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */
45 
46 
47 #define SBULL_MAJOR 0       /* dynamic major by default */
48 #define SBULL_DEVS 2        /* two disks */
49 #define SBULL_RAHEAD 2      /* two sectors */
50 #define SBULL_SIZE 2048     /* two megs each */
51 #define SBULL_BLKSIZE 1024  /* 1k blocks */
52 #define SBULL_HARDSECT 512  /* 2.2 and 2.4 can used different values */
53 
54 #define SBULLR_MAJOR 0      /* Dynamic major for raw device */
55 /*
56  * The sbull device is removable: if it is left closed for more than
57  * half a minute, it is removed. Thus use a usage count and a
58  * kernel timer
59  */
60 
61 typedef struct Sbull_Dev {
62    int size;
63    int usage;
64    struct timer_list timer;
65    spinlock_t lock;
66    u8 *data;
67 #ifdef SBULL_MULTIQUEUE
68    request_queue_t *queue;
69    int busy;
70 #endif
71 }              Sbull_Dev;
72 


  1 /*
  2  * Sample disk driver, from the beginning.
  3  */
  4 
  5 #include <linux/config.h>
  6 #include <linux/module.h>
  7 #include <linux/moduleparam.h>
  8 #include <linux/init.h>
  9 
 10 #include <linux/sched.h>
 11 #include <linux/kernel.h>    /* printk() */
 12 #include <linux/slab.h>        /* kmalloc() */
 13 #include <linux/fs.h>        /* everything... */
 14 #include <linux/errno.h>    /* error codes */
 15 #include <linux/timer.h>
 16 #include <linux/types.h>    /* size_t */
 17 #include <linux/fcntl.h>    /* O_ACCMODE */
 18 #include <linux/hdreg.h>    /* HDIO_GETGEO */
 19 #include <linux/kdev_t.h>
 20 #include <linux/vmalloc.h>
 21 #include <linux/genhd.h>
 22 #include <linux/blkdev.h>
 23 #include <linux/buffer_head.h>    /* invalidate_bdev */
 24 #include <linux/bio.h>
 25 
 26 MODULE_LICENSE("Dual BSD/GPL");
 27 
 28 static int sbull_major = 0;
 29 module_param(sbull_major, int, 0);
 30 static int hardsect_size = 512;
 31 module_param(hardsect_size, int, 0);
 32 static int nsectors = 1024;    /* How big the drive is */
 33 module_param(nsectors, int, 0);
 34 static int ndevices = 4;
 35 module_param(ndevices, int, 0);
 36 
 37 /*
 38  * The different "request modes" we can use.
 39  */
 40 enum {
 41     RM_SIMPLE  = 0,    /* The extra-simple request function */
 42     RM_FULL    = 1,    /* The full-blown version */
 43     RM_NOQUEUE = 2,    /* Use make_request */
 44 };
 45 static int request_mode = RM_SIMPLE;
 46 module_param(request_mode, int, 0);
 47 
 48 /*
 49  * Minor number and partition management.
 50  */
 51 #define SBULL_MINORS    16
 52 #define MINOR_SHIFT    4
 53 #define DEVNUM(kdevnum)    (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT
 54 
 55 /*
 56  * We can tweak our hardware sector size, but the kernel talks to us
 57  * in terms of small sectors, always.
 58  */
 59 #define KERNEL_SECTOR_SIZE    512
 60 
 61 /*
 62  * After this much idle time, the driver will simulate a media change.
 63  */
 64 #define INVALIDATE_DELAY    30*HZ
 65 
 66 /*
 67  * The internal representation of our device.
 68  */
 69 struct sbull_dev {  //描述sbull设备的结构体
 70         int size;                       /* Device size in sectors */
 71         u8 *data;                       /* The data array */
 72         short users;                    /* How many users */
 73         short media_change;             /* Flag a media change? */
 74         spinlock_t lock;                /* For mutual exclusion */
 75         struct request_queue *queue;    /* The device request queue */
 76         struct gendisk *gd;             /* The gendisk structure */
 77         struct timer_list timer;        /* For simulated media changes */
 78 };
 79 
 80 static struct sbull_dev *Devices = NULL;
 81 
 82 /*
 83  * Handle an I/O request.
 84  */
 85 static void sbull_transfer(struct sbull_dev *dev, unsigned long sector,
 86         unsigned long nsect, char *buffer, int write)
 87 {
 88     unsigned long offset = sector*KERNEL_SECTOR_SIZE;
 89     unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE;
 90 
 91     if ((offset + nbytes) > dev->size) {
 92         printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
 93         return;
 94     }
 95     if (write)
 96         memcpy(dev->data + offset, buffer, nbytes);
 97     else
 98         memcpy(buffer, dev->data + offset, nbytes);
 99 }
100 
101 /*
102  * The simple form of the request function.
103  * 块设备的驱动程序的核心是它的请求函数,至少如设备的启动都是在这个函数里面完成的...
104  * 编写一个简单的request如下,但是驱动程序如果要在很高的层析上控制非常复杂的硬件,它将不再这么简单
105  *
106  * 内核需要驱动程序处理读取写入以及其他的设备操作时,就会调用该函数
107  * request是在一个原子上下文运行的,该锁是由内核控制的,拥有锁时防止内核为设备安排其他请求
108  * 
109  */
110 static void sbull_request(request_queue_t *q)  
111 {        // 每一个设备都需要一个请求队列,因为对磁盘的数据实际的传入和传出的时间 与内核请求的时间相差很大
112          // 驱动程序所需要知道的任何信息都敖汉在通过请求队列传给我们的结构中
113          // 请求队列保存了表述设备所能处理的请求的参数:最大尺寸,在同一个请求所能包含的独立段的数目、硬件扇区大小、对齐需求等等
114          // 请求队列还实现了插件接口,使多规格IO调度器的使用称为可能         
115     struct request *req;
116 
117     while ((req = elv_next_request(q)) != NULL) {
118         // elv_next_request 返回队列中第一个未完成的请求,如果没有请求返回null
119         // 这个函数并不从队列中删除请求,如果不加以干涉而两次调用该函数,则两次放回相同的结构
120         struct sbull_dev *dev = req->rq_disk->private_data;
121         if (! blk_fs_request(req)) {
122         // blk_fs_request告诉用户该请求是否是一个文件系统请求——移动块设备数据请求
123             printk (KERN_NOTICE "Skip non-fs request\n");
124             end_request(req, 0);
125             continue;
126         }
127     //        printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
128     //                dev - Devices, 
129     //                rq_data_dir(req),传输方向,0表述从设备读,非0表示写
130     //                req->sector,开始扇区的索引号
131     //                req->current_nr_sectors, 需要传输的扇区数
132     //                req->flags);
133     //              sbull_transfer 传输的数据的缓冲区指针
134     //              实现数据的真正移动
135         sbull_transfer(dev, req->sector, req->current_nr_sectors,
136                 req->buffer, rq_data_dir(req));
137         end_request(req, 1);
138     }
139 }
140 
141 
142 /*
143  * Transfer a single BIO.
144  * 该函数遍历了bio结构中的每个段,获得内核虚拟地址以访问缓冲区,然后调用前面介绍过的sbull_transfer函数,以完成数据的拷贝.
145  */
146 static int sbull_xfer_bio(struct sbull_dev *dev, struct bio *bio)
147 {
148     int i;
149     struct bio_vec *bvec;
150     sector_t sector = bio->bi_sector;
151 
152     /* Do each segment independently. */
153     bio_for_each_segment(bvec, bio, i) {
154         char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
155         sbull_transfer(dev, sector, bio_cur_sectors(bio),
156                 buffer, bio_data_dir(bio) == WRITE);
157         sector += bio_cur_sectors(bio);
158         __bio_kunmap_atomic(bio, KM_USER0);
159     }
160     return 0; /* Always "succeed" */
161 }
162 
163 /*
164  * Transfer a full request.
165  * 实际执行请求的函数
166  */
167 static int sbull_xfer_request(struct sbull_dev *dev, struct request *req)
168 {
169     struct bio *bio;
170     int nsect = 0;
171     
172     rq_for_each_bio(bio, req) {  // rq_for_each_bio宏 只是遍历了请求中的每个biio结构,并提供可可以传递给sbull_xfer_bio函数用于传输的指针
173         sbull_xfer_bio(dev, bio);
174         nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
175     }
176     return nsect;
177 }
178 
179 
180 
181 /*
182  * Smarter request function that "handles clustering".
183  * 这个函数将注册一个bio请求函数
184  */
185 static void sbull_full_request(request_queue_t *q)
186 {
187     struct request *req;
188     int sectors_xferred;
189     struct sbull_dev *dev = q->queuedata;
190 
191     while ((req = elv_next_request(q)) != NULL) {
192         if (! blk_fs_request(req)) {
193             printk (KERN_NOTICE "Skip non-fs request\n");
194             end_request(req, 0);
195             continue;
196         }
197         sectors_xferred = sbull_xfer_request(dev, req);
198         if (! end_that_request_first(req, 1, sectors_xferred)) {
199             blkdev_dequeue_request(req);
200             end_that_request_last(req);
201         }
202     }
203 }
204 
205 
206 
207 /*
208  * The direct make request version.
209  */
210 static int sbull_make_request(request_queue_t *q, struct bio *bio)
211 {
212     struct sbull_dev *dev = q->queuedata;
213     int status;
214 
215     status = sbull_xfer_bio(dev, bio);
216     bio_endio(bio, bio->bi_size, status);
217     return 0;
218 }
219 
220 
221 /*
222  * Open and close.
223  * // sbull必须知道最后一个用户何时关闭了设备
224  */
225 
226 static int sbull_open(struct inode *inode, struct file *filp)
227 {
228     struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
229 
230     del_timer_sync(&dev->timer);
231     filp->private_data = dev;
232     spin_lock(&dev->lock);
233     if (! dev->users) 
234         check_disk_change(inode->i_bdev);
235     dev->users++;
236     spin_unlock(&dev->lock);
237     return 0;
238 }
239 /*
240  * 减少用户基数,并启用介质移除定时器
241  */
242 static int sbull_release(struct inode *inode, struct file *filp)
243 {
244     struct sbull_dev *dev = inode->i_bdev->bd_disk->private_data;
245 
246     spin_lock(&dev->lock);
247     dev->users--;
248 
249     if (!dev->users) {
250         dev->timer.expires = jiffies + INVALIDATE_DELAY; //30秒的定时器,如果这个时段内设备没有被打开则移除设备
251         add_timer(&dev->timer);
252     }
253     spin_unlock(&dev->lock);
254 
255     return 0;
256 }
257 
258 /*
259  * Look for a (simulated) media change.
260  */
261 int sbull_media_changed(struct gendisk *gd)
262 {
263     struct sbull_dev *dev = gd->private_data;
264     
265     return dev->media_change;
266 }
267 
268 /*
269  * Revalidate.  WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
270  * with open.  That needs to be reevaluated.
271  * 调用此函数内核将试着重新读取分区表,在这里这个函数只是简单地重置了media_change的标志位,并清除内存空间以模拟插入一张磁盘
272  */
273 int sbull_revalidate(struct gendisk *gd)
274 {
275     struct sbull_dev *dev = gd->private_data;
276     
277     if (dev->media_change) {
278         dev->media_change = 0;
279         memset (dev->data, 0, dev->size);
280     }
281     return 0;
282 }
283 
284 /*
285  * The "invalidate" function runs out of the device timer; it sets
286  * a flag to simulate the removal of the media.
287  */
288 void sbull_invalidate(unsigned long ldev)
289 {
290     struct sbull_dev *dev = (struct sbull_dev *) ldev;
291 
292     spin_lock(&dev->lock);
293     if (dev->users || !dev->data) 
294         printk (KERN_WARNING "sbull: timer sanity check failed\n");
295     else
296         dev->media_change = 1;
297     spin_unlock(&dev->lock);
298 }
299 
300 /*
301  * The ioctl() implementation
302  * ioctl函数在这里只处理了一个命令,对设备物理信息的查询请求
303  * 这里由于是虚拟设备,因此只提供了一些虚拟信息
304  */
305 
306 int sbull_ioctl (struct inode *inode, struct file *filp,
307                  unsigned int cmd, unsigned long arg)
308 {
309     long size;
310     struct hd_geometry geo;
311     struct sbull_dev *dev = filp->private_data;
312 
313     switch(cmd) {
314         case HDIO_GETGEO:
315             /*
316          * Get geometry: since we are a virtual device, we have to make
317          * up something plausible.  So we claim 16 sectors, four heads,
318          * and calculate the corresponding number of cylinders.  We set the
319          * start of data at sector four.
320          */
321         size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
322         geo.cylinders = (size & ~0x3f) >> 6;
323         geo.heads = 4;
324         geo.sectors = 16;
325         geo.start = 4;
326         if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
327             return -EFAULT;
328         return 0;
329     }
330 
331     return -ENOTTY; /* unknown command */
332 }
333 
334 
335 
336 /*
337  * The device operations structure.
338  */
339 static struct block_device_operations sbull_ops = {
340     .owner           = THIS_MODULE,
341     .open              = sbull_open,
342     .release      = sbull_release,
343     .media_changed   = sbull_media_changed,
344     .revalidate_disk = sbull_revalidate,
345     .ioctl             = sbull_ioctl
346 };
347 
348 
349 /*
350  * Set up our internal device.初始化sbull_dev结构
351  */
352 static void setup_device(struct sbull_dev *dev, int which)
353 {
354     /*
355      * Get some memory.
356      */
357     memset (dev, 0, sizeof (struct sbull_dev));
358     dev->size = nsectors*hardsect_size;
359     dev->data = vmalloc(dev->size);
360     if (dev->data == NULL) {
361         printk (KERN_NOTICE "vmalloc failure.\n");
362         return;
363     }
364     spin_lock_init(&dev->lock);
365     
366     /*
367      * The timer which "invalidates" the device.
368      */
369     init_timer(&dev->timer);
370     dev->timer.data = (unsigned long) dev;
371     dev->timer.function = sbull_invalidate;
372     
373     /*
374      * The I/O queue, depending on whether we are using our own
375      * make_request function or not.
376      */
377     switch (request_mode) {
378         case RM_NOQUEUE:
379         dev->queue = blk_alloc_queue(GFP_KERNEL);
380         if (dev->queue == NULL)
381             goto out_vfree;
382         blk_queue_make_request(dev->queue, sbull_make_request);
383         break;
384 
385         case RM_FULL:
386         dev->queue = blk_init_queue(sbull_full_request, &dev->lock); 创建iqngqiu队列
387         if (dev->queue == NULL)
388             goto out_vfree;
389         break;
390 
391         default:
392         printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode);
393             /* fall into.. */
394     
395         case RM_SIMPLE:
396         dev->queue = blk_init_queue(sbull_request, &dev->lock);
397         //sbull_request是请求函数,负责执行块设备的读写请求。
398         if (dev->queue == NULL)
399             goto out_vfree;
400         break;
401     }
402     blk_queue_hardsect_size(dev->queue, hardsect_size); //设置扇区大小
403     dev->queue->queuedata = dev;
404     /*
405      * And the gendisk structure.
406      * 拥有了设备内存和请求队列,就可以分配、初始化及安装相应的gendisk结构了.
407      */
408     dev->gd = alloc_disk(SBULL_MINORS);//SBULL_MINORS是每个sbull设备所支持的次设备号的数量
409     if (! dev->gd) {
410         printk (KERN_NOTICE "alloc_disk failure\n");
411         goto out_vfree;
412     }
413     dev->gd->major = sbull_major;
414     dev->gd->first_minor = which*SBULL_MINORS;
415     dev->gd->fops = &sbull_ops;
416     dev->gd->queue = dev->queue;
417     dev->gd->private_data = dev;
418     snprintf (dev->gd->disk_name, 32, "sbull%c", which + 'a');
419     set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE));//每个请求的大小都是扇区大小的整数倍,内核总是认为扇区大小是512字节,因此必须进行转换
420     //KERNEL_SECTOR_SIZE是本地定义的一个常量,使用该常量进行内核512自己上去到实际扇区大小的转换。
421     
422     add_disk(dev->gd);//结束设置过程
423     return;
424 
425   out_vfree:
426     if (dev->data)
427         vfree(dev->data);
428 }
429 
430 
431 
432 static int __init sbull_init(void)
433 {
434     int i;
435     /*
436      * Get registered.
437      */
438     sbull_major = register_blkdev(sbull_major, "sbull"); //动态分配主设备号
439     if (sbull_major <= 0) {
440         printk(KERN_WARNING "sbull: unable to get major number\n");
441         return -EBUSY;
442     }
443     /*
444      * Allocate the device array, and initialize each one.
445      */
446     Devices = kmalloc(ndevices*sizeof (struct sbull_dev), GFP_KERNEL);
447     if (Devices == NULL)
448         goto out_unregister;
449     for (i = 0; i < ndevices; i++) 
450         setup_device(Devices + i, i);//初始化sbull_dev结构
451     
452     return 0;
453 
454   out_unregister:
455     unregister_blkdev(sbull_major, "sbd");
456     return -ENOMEM;
457 }
458 
459 static void sbull_exit(void)
460 {
461     int i;
462 
463     for (i = 0; i < ndevices; i++) {
464         struct sbull_dev *dev = Devices + i;
465 
466         del_timer_sync(&dev->timer);
467         if (dev->gd) {
468             del_gendisk(dev->gd);
469             put_disk(dev->gd);
470         }
471         if (dev->queue) {
472             if (request_mode == RM_NOQUEUE)
473                 blk_put_queue(dev->queue);
474             else
475                 blk_cleanup_queue(dev->queue);// 删除请求队列
476         }
477         if (dev->data)
478             vfree(dev->data);
479     }
480     unregister_blkdev(sbull_major, "sbull");
481     kfree(Devices);
482 }
483     
484 module_init(sbull_init);
485 module_exit(sbull_exit);
486 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值