资源预览内容
第1页 / 共3页
第2页 / 共3页
第3页 / 共3页
亲,该文档总共3页全部预览完了,如果喜欢就下载吧!
资源描述
如果只是为了应付考试,这个文档就太啰嗦了,不用看,不过还是可以帮助记忆,考试只会考其中加粗字体的几个函数中的一个,至于是哪个我不能断定,因此要记的还是比较多的,要是能理解就更好了,结合课本和下面的解释应该能大体上弄明白这个虚拟块设备驱动的实现过程,毕竟设备驱动是内核的一部分,光看下面的解释也是还是很头晕的,不过坚持看下去还是有收获的,我也差不多花了半天时间,不过,要是打算的话就可以直接跳过了。#define MAJOR_NR 70 /我们创造的虚拟块设备的主设备号#define DEVICE_NAME “bdemo” /我们创造的虚拟块设备的名字,当设备加载成功后可用 lsmod 命令查看到该设备模块名#define blkdemo_devs 2 /虚拟块设备的个数#define blkdemo_rahead 2 /读取块设备时预读的扇区个数#define blkdemo_size 4 /每个虚拟块设备的大小,单位为 KB#define blkdemo_blksize 1024 /设备每个数据块的大小,即 block,单位为字节#define blkdemo_hardsect 512 /设备每个扇区的大小,单位为字节struct blkdemo_device / 这里定义了我们将要创造的虚拟块设备的数据结构int size; / 用来记录真实块设备的容量,即下面 data 指针所指向数据存储区的大小int use_cnt; / 用来记录正在使用该块设备的程序的个数int hardsect; / 用来保存该块设备每个扇区的大小,单位为字节,即设备的使用计数u8 *data; / 该指针所指向的内存区域就是该块设备真正用来存储数据的区域,在该设备还未被加载函数初始化时,该指针为/ 空,即系统还没有为该设备分配内存区域。;static int blkdemo_sizesblkdemo_devs; /用来保存我们创建的所有虚拟块设备的大小,单位为 KBstatic int blkdemo_blksizesblkdemo_devs; /用来保存我们创建的所有虚拟块设备中每个数据块的大小,单位为字节static int blkdemo_hardsectsblkdemo_devs;/用来保存我们创建的所有虚拟块设备中每个扇区的大小,单位为字节/上面的这三个数组将会在我们加载这些设备时被注册到内核的数据结构中(即让内核中与之相关的一些指针指向它们,让内核能够读取我们所创建的设备的一些重要信息/对于一个新的设备,内核肯定不知道他为何物,要想让内核识别我们自己创造的设备,则必须将该设备的一些信息、使用这个设备的方/法等告诉内核,由于内核早已编译成型,至于如何去告诉内核就早已模式化。内核中有几个指针数组(书本 page81)专门用来完成上面的部分任务:/ blk_size; / blksize_size;/ hardsect_size;/ read_ahead;/这几个数组都为每一个主设备号留有一个位置,对于 2.4 的内核,主设备号和次设备号均用 8 位二进制来表示(即短整型的高八位和/低八位) ,因此这几个数组都包含有 256 个元素,每个元素都是与主设备号对应的一个指针,如果主设备号所对应的设备不存在,则该/指针置为空 (NULL),其实其中很多指针都为空,因为一般电脑上都没有那么多不同类型的块设备,当然,对于我们所创造的这个块设/备而言,它与系统中所存在的其他块设备的类型都不同,要为其确定一个主设备号,这个没什么硬性的规定,只要找一个没被使用的主/设备号就可以了,这个程序中使用的是 70(前面的 MOJOR_NR 宏)。上面我们定义了保存有虚拟块设备信息的数组,现在只要将他们的/首地址赋给这几个数组中下标 70(主设备号)所对应的指针元素即可。这一过程是在后面的加载函数中完成的。static int blksize = blkdemo_blksize;struct blkdemo_device blkdemo_devblkdemo_devs;/这里才真正创建了我们虚拟块设备对应的结构体变量(一个全局数组) ,/每个元素为对应一个虚拟块设备虚拟块设备的打开函数(open():int blkdemo_open(struct inode *inode, strcut file *filp) /设备文件对应的节点(inode)结构中包含有对应的设备号int num;num = DEVICE_NR(inode-i_rdev); /用 DEVICE_NR 宏可求出该节点所对应设备的次设备号,所以 num 即为次设备号if (!blkdemo_devnum.use_cnt) /如果该设备的使用计数为 0,则说该设备没有被任何程序使用,当虚拟块设备没有被/任何程序使用时,内核先前为该设备所分配的存储区很可能已经被释放掉了,甚至对于可移动设备而言,有可能该设备都被拔掉了(当/然,我们的虚拟块设备是不可能的) ,因此,在打开该设备时要进行严格的检查,不然会导致设备打开出错而造成系统崩溃。check_disk_change(inode-i_rdev);/首先检查该块设备是否发生了变化,比如已经被移除了(该设备不可能,所以/此处没有用 if 来判断,只是形式的调用了一下该函数。if (!blkdemo_devnum.data)/然后判断该设备的数据存储区域是否已经被释放掉了return ENOMEM; /如果是,则返回,告知系统该设备无法打开,-ENOMEM 是一个内核中定义的宏,它代表的意思是/“error,no memory”。/如果上述情况均未发生,一切正常,则打开设备,对于这个虚拟的块设备,其实没有什么好打开的,不过还是意思一下:blkdemo_devnum.use_cnt+; /将设备的使用计数加 1,表示又多了个程序使用该设备。MOD_INC_USE_COUNT; /并且将内核所管理的模块使用计数也加 1,好让内核也知道多了一个程序使用该虚拟设备模块。模块使/用计数是内核管理模块时要用的,只有当一个设备的模块使用计数为 0 时才能卸载该模块,这个值也可以通过 lsmod 命令查看到return(0);/返回 0,表示设备已成功打开虚拟块设备的释放函数(release():int blkdemo_release(struct inode *inode, struct file *filp)/释放并不代表将此设备从内核中移除了,他是对调用它的程序而言的,只表示这个程序不再使用该设备了int num;num = DEVICE_NR(inode-i_rdev);/求出设备的次设备号blkdemo_devnum.use_cnt-;/既然使用该设备的程序少了一个,则应该将该设备的使用计数减 1MOD_DEC_USE_COUNT;/ 并且将内核所管理的模块使用计数也减 1return(0);/返回 0,表示设备释放成功虚拟块设备的请求函数(request():void blkdemo_request(request_queue_t *q) /块设备和字符设备在数据的读写是有区别的。对于块设备,程序对数据读写的请求一般不会立即得到回应,程序首先要提出对数据/的请求,此时内核会动态的分配一个 request 结构(page74,图 7-1 中有 request 结构的抽象描述) ,并将请求的详细信息记录到/这个 request 结构中,然后将这个结构按照某些规则插入到该设备的请求队列中,当系统处于较为合适的状态时,内核就会对队列上/的所有请求进行集中处理。采用这些繁琐的方法都由真实块设备的物理特性决定的,因为大部分块设备都和硬盘类似,读取数据时要进/行寻道等一些复杂的命令操作,而这是一个相当耗时的过程,如果每当有程序请求数据时内核就立即去操作磁盘,那么系统大部分宝贵/的时间都被消耗在了等待磁盘响应上了,因此内核中构建了一套专门操作块设备的方法,来对请求的数据进行集中处理,以提高磁盘的/吞吐量和系统的整体性能,request()函数的任务就是按顺序处理这条请求队列,直到队尾,/除非出现意外错误而返回。struct request *req;int res = 1; /用来记录对当前请求的处理是否成功,成功则置 1,失败则清 0,供后面的 end_request()函数使用。int num;int size;u8 *ptr;while (1) INIT_REQUEST; /测试当前的请求是否有效req = CURRENT; /CURRENT 指针由内核中的 end_request()函数管理,它指向请求队列中当前要处理的 request 结构。num = DEVICE_NR(req-rq_dev);/获取所请求的设备的次设备号ptr = blkdemo_devnum.data + req-sector * blkdemo_devnum.hardsect;/ 设备数据存储区的首地址 + 请求的首个扇区 * 该设备每个扇区的字节大小,最后,ptr 指向所要请求的数据size = req-current_nr_sectors * blkdemo_devnum.hardsect;/ 当前请求的扇区总数 * 该设备每个扇区的字节大小,因此 size 为当前请求所请求的总字节数if (ptr + size blkdemo_devnum.data + blkdemo_devnum.size) /判断所请求数据的地址是否超出/了该设备的数据存储区的范围。超出范围后会导致内存溢出,造成系统崩溃,决不能容忍。printk(KERN_WARNNING “blkdemo: request past end of devicen”);/向控制台或日志打印出警告信息res = 0; /请求失败, res 置 0/如果正常则下面打印出当前请求的详细信息(仅调试时使用,可以不写)printk(“ request %p: cmd %i sec %li (nr.%li)n”, req,/和 KERN_ALERT 是等价的,这是内核中定req-cmd, req-sector, req-current_nr_sectors);/义的 8 种日志级别宏之一(page43)switch (cmd) /判断当前请求要对所请求的数据做何种操作case READ:/如果是读,则,memcpy(req-buffer,ptr, size);/把从 ptr 开始的 size 字节复制到发出该请求的程序所提供的缓冲区中去res = 1;/完成了请求, res 置 1break;case WRITE:/如果是写,则,memcpy(ptr, req-buffer, size);/把发出该请求的程序的缓冲区中的数据复制到该设备中 ptr 所指向的内存区res = 1; /完成了请求, res 置 1break;default: /未知请求res = 0;/无法完成, res 置 0end_request(res);/根据请求是否成功来调整 CURRENT 指针变量的值,为处理请求队列中的下一个请求作准备struct block_device_operations blkdemo_bdops = /初始化虚拟块设备操作函数接口open: blkdemo_open,release: blkdemo_release,;虚拟块设备的加载函数:static int _init blkdemo_init(void)/_init 为
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号