Word可编辑
一、 课程设计目的
通过操作系统设计可以学习操作系统的原理和设计技巧。本次课程设计所选的是设备驱动程序,通过此次课程设计可以掌握Linux操作系统的使用方法;了解Linux系统内核代码结构;掌握实例操作系统的实现方法。
具体为:掌握设备驱动程序的编写、编译和装载、卸载方法,了解设备文件的创建,并知道如何编写测试程序测试自己的驱动程序是否能够正常工作。
二、 课程设计内容与要求
1、 编写一个简单的块设备驱动程序,该块设备包括打开、IO控制与释放三个基本操作。 2、 编写一个测试程序,测试字符设备驱动程序的正确性。
3、 要求在实验报告中列出Linux内核的版本与内核模块加载过程
三、 系统分析与设计
1、 系统分析
所有设备控制操作都是由与被控制设备相关的代码来完成的,这段代码就叫做驱动程序;而一个块驱动程序主要通过传输固定大小的随机数据来访问设备。
这次的驱动程序只是编写一个虚拟程序,并不需要去驱动硬件,通过一系列的注册函数,使内核知道设备的存在。
内核模块的编写,首先是进行编写函数入口,对函数进行加载,module_init(),然后到加载函数中编写操作。
块设备的操作主要包括:设备的打开、IO控制与释放;
最后,对模块进行卸载,调用module_exit(),在卸载模块中对一些内存进行释放。
2、 系统设计
2.1 概要设计:
2.1.1 函数加载模块:
在函数加载模块中,实现对块设备的注册、对sbull_dev数据结构的初始化;对请求队列的分配;以及设置扇区大小和分配、初始化及安装相应的gendisk结构。
函数声明:static int __init sbull_init(void);
2.1.2 函数操作模块: 设备打开模块:
函数声明:static int sbull_open(struct inode *inode,struct file *filp);
IO控制模块:
函数声明:static int sbull_release(struct inode *inode,struct file *filp);
设备释放模块:
函数声明:static int sbull_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
2.1.3 函数卸载模块:
在函数卸载模块中实现对申请内存的释放和对请求队列的删除。 函数声明:static void __exit sbull_exit(void);
Word可编辑
2.2 详细设计:
2.2.1 函数加载模块具体功能实现:
首先,对块设备驱动程序进行注册, int register_blkdev(unsigned int major,const char *name);,获得主设备号。
然后,对sbull_dev数据结构进行内存的分配和初始化工作: Devices = kmalloc(sizeof(struct sbull_dev),GFP_KERNEL); memset(Devices,0,sizeof(struct sbull_dev)); Devices->size = nsectors * hardsect_size; Devices->data = vmalloc(Devices->size); memset(Devices->data,0,Devices->size);
对自旋锁进行分配和初始化: spin_lock_init(&Devices->lock);
分配请求队列:
Devices->queue = blk_init_queue(sbull_request,&Devices->lock);
设置扇区大小:
blk_queue_logical_block_size(Devices->queue, hardsect_size);
分配、初始化及安装相应的gendisk结构 Devices->gd = alloc_disk(1);
Devices->gd->major = sbull_major; Devices->gd->first_minor = 0; Devices->gd->fops = &sbull_ops;
Devices->gd->queue = Devices->queue; Devices->gd->private_data = Devices; strcpy(Devices->gd->disk_name,\
set_capacity(Devices->gd,nsectors * (hardsect_size / KERN_SECTOR_SIZE)); 最后,调用add_disk()结束设置过程。 add_disk(Devices->gd);
2.2.2 函数操作模块的具体实现:
在块设备驱动程序中,主要有设备的打开、IO控制与释放三个基本操作。而这三个操作是通过block_device_operations结构中的sbull_open、sbull_release、sbull_ioctl实现的。
其中,因为此次的块设备驱动程序只是虚拟内存,而没有操作实际的硬件,在打开和、释放操作中并没有具体的功能。
而在IO控制中,主要实现了得到主设备号、得到扇区总数、得到块设备大小的操作功能。
在IO控制中,首先对参数进行了检测: //检测命令的有效性
if(_IOC_TYPE(cmd) != SBULL_IOC_MAGIC) { return -EINVAL; }
if(_IOC_NR(cmd) > SBULL_IOC_MAXNR) {
Word可编辑
return -EINVAL; }
//根据命令类型,检测用户空间是否可以访问 if(_IOC_DIR(cmd) & _IOC_READ) { err = !access_ok(VERIFY_WRITE,(void*)arg,_IOC_SIZE(cmd)); }
else if(_IOC_DIR(cmd) & _IOC_WRITE) { err = !access_ok(VERIFY_READ,(void*)arg,_IOC_SIZE(cmd)); } if(err) { return -EFAULT; }
然后,根据传递参数cmd的命令来实现具体功能: switch(cmd) { case SBULL_IOCGETMAJOR: //得到主设备号 ret = __put_user(sbull_major,(int __user*)arg); break; case SBULL_IOCGETSECTORS: //得到扇区总数 ret = __put_user(nsectors,(int __user*)arg); break; case SBULL_IOCGETSIZE: //得到块设备大小 ret = __put_user(nsectors * hardsect_size,(int __user*)arg); break; default: return -EFAULT; }
2.2.3 函数卸载模块具体实现: 对gendisk进行释放: if(dev->gd) { del_gendisk(dev->gd); put_disk(dev->gd); }
对队列进行清除: if(dev->queue) { blk_cleanup_queue(dev->queue); printk(\}
清除设备信息:
Word可编辑
if(dev->data) { vfree(dev->data); printk(\}
对块设备进行注销:
unregister_blkdev(sbull_major,\对设备内存进行释放: kfree(Devices);
3、 模块设计
3.1 装载模块
3.2 设备操作模块:打开、释放、IO控制 3.3 卸载模块
3.4程序模块之间的层次关系:
模块装载 打开 释放 IO控制 模块卸载
4、 数据结构说明
4.1 块设备操作接口结构
static struct block_device_operations sbull_ops = { .owner = THIS_MODULE, .open = sbull_open, .release= sbull_release, .ioctl = sbull_ioctl, };
4.2 块设备结构描述 struct sbull_dev { int size; //以扇区为单位设备的大小 u8 *data; //数据数组(存储磁盘数据队列) struct request_queue *queue; //请求队列(用于互斥) struct gendisk *gd; //gendisk结构 spinlock_t lock; //设备自旋锁 };
Word可编辑
四、 系统测试与调试分析
4.1 系统测试: 测试方法:黑盒测试 测试技术:功能测试 测试用例表:
测试名称 块设备驱动程序 测试块设备驱动程序能否正常工作 功能测试 黑盒测试 块设备的IO控制操作 将块设备驱动程序加载,然后执行test程序测试 块设备驱动程序加载后,将在/proc/devices中sbull的主设备号与test测试终端输出的主设备号进行对比,判断IO控制的正确性。 251 与预期相符 测测试目的 试说明 测试技术 测试方法 测试内容 测测试步骤 试用测试数据 例 预期结果 测试结果 4.2 调试分析: 4.2.1 最初始,对块驱动程序设计并不了解,不知该从何下手。 解决方法:通过上网查资料和向接触过Linux的同学请教,大致了解了使用Linux编程的基础,然后,通过学习《Linux驱动程序》这本书,学习到驱动程序并不像应用程序那样,从主函数开始进行的,而是一种模块化的设计方法。通过模块入口函数进行加载,然后通过各自的结构调用所需要的模块,完成相应的操作后,通过卸载模块来结束程序。
4.2.2 在编写程序过程中,因为内核版本的问题,许多参考书中的函数不能正常工作。 解决方法:通过上网查资料,对因版本变动而不能用的几个函数,有的找到了最新版本的代替者,有的是通过定义相应的宏来解决的。
五、 用户手册
5.1 使用平台:Linux系统 版本为ubantu10.10 5.2 内核版本:2.6.35 5.3 操作步骤: 5.3.1 编译:make
5.3.2 文件加载:insmod sbull.ko 5.3.3 用lsmod查看:lsmod