操作系统课程设计报告.docx
- 文档编号:11118447
- 上传时间:2023-05-29
- 格式:DOCX
- 页数:25
- 大小:1.37MB
操作系统课程设计报告.docx
《操作系统课程设计报告.docx》由会员分享,可在线阅读,更多相关《操作系统课程设计报告.docx(25页珍藏版)》请在冰点文库上搜索。
操作系统课程设计报告
操作系统课程设计报告
组长:
赵书剑0909102518
组员:
鲁勇0909103006
组员:
安哲民0909103010
班级:
计科1005班
指导老师:
夏佳志
2013年7月3日
字符驱动设备设计
1、课程设计目的
Linux系统的开源性使其在嵌入式系统的开发中得到了越来越广泛的应用,但其本身并没有对种类繁多的硬件设备都提供现成的驱动程序,特别是由于工程应用中的灵活性,其驱动程序更是难以统一,这时就需开发一套适合于自己产品的设备驱动。
对用户而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说是把设备映射为一个特殊的设备文件,用户程序可以像对其它文件一样对此设备文件进行操作。
通过这次课程设计可以了解linux的模块机制,懂得如何加载模块和卸载模块,进一步熟悉模块的相关操作。
加深对驱动程序定义和设计的了解,了解linux驱动的编写过程,提高自己的动手能力。
2、课程设计内容与要求
(1)设计WindowsXP或者Linux操作系统下的设备驱动程序;
(2)设备类型可以是字符设备、块设备或者网络设备;
(3)设备可以是虚拟的也可以是实际设备;
3、系统分析与设计
(1)系统分析
系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。
设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。
设备驱动程序是内核的一部分,它完成以下的功能:
1、对设备初始化和释放;
2、把数据从内核传送到硬件和从硬件读取数据;
3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据;
4、检测和处理设备出现的错误。
Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得Windows的设备操作犹如文件一般。
在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,如open()、close()、read()、write()等。
Linux主要将设备分为二类:
字符设备和块设备。
字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式进行。
字符设备提供给应用程序的是一个流控制接口,主要包括open、close(或release)、read、write、ioctl、poll和mmap等。
在系统中添加一个字符设备驱动程序,实际上就是给上述操作添加对应的代码。
对于字符设备和块设备,Linux内核对这些操作进行了统一的抽象,把它们定义在结构体file_operations中。
在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源),可能会引发"竞态",因此我们必须对共享资源进行并发控制。
Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。
自旋锁与信号量"类似而不类",类似说的是它们功能上的相似性,"不类"指代它们在本质和实现机理上完全不一样,不属于一类。
自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已经释放了锁,"自旋"就是"在原地打转"。
而信号量则引起调用者睡眠,它把进程从运行队列上拖出去,除非获得锁。
这就是它们的"不类"。
但是,无论是互斥信号量,还是自旋锁,在任何时刻,最多只能有一个保持者,即在任何时刻最多只能有一个执行单元获得锁。
这就是它们的"类似"。
鉴于自旋锁与信号量的上述特点,一般而言,自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用;信号量适合于保持时间较长的情况,只能在进程上下文使用。
如果被保护的共享资源只在进程上下文访问,则可以以信号量来保护该共享资源,如果对共享资源的访问时间非常短,自旋锁也是好的选择。
但是,如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。
阻塞操作是指,在执行设备操作时,若不能获得资源,则进程挂起直到满足可操作的条件再进行操作。
非阻塞操作的进程在不能进行设备操作时,并不挂起。
被挂起的进程进入sleep状态,被从调度器的运行队列移走,直到等待的条件被满足。
在Linux驱动程序中,我们可以使用等待队列(waitqueue)来实现阻塞操作。
waitqueue很早就作为一个基本的功能单位出现在Linux内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。
等待队列可以用来同步对系统资源的访问,上节中所讲述Linux信号量在内核中也是由等待队列来实现的。
结合阻塞与非阻塞访问、poll函数可以较好地解决设备的读写,但是如果有了异步通知就更方便了。
异步通知的意思是:
一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上"中断"地概念,比较准确的称谓是"信号驱动(SIGIO)的异步I/O"。
在本课程设计中主要实现一个简单的字符驱动设备,运用信号量和自旋锁进行相关的并发控制。
(2)系统设计
2.1测试环境:
系统:
linuxmint15
kernelversion:
3.8.0-23-generic
gccversion:
4.7.3
2.2模块设计:
2.3数据结构说明:
字符设备驱动主要应用了三种数据结构:
file_operations结构,这是设备驱动程序所提供的一组用一个结构向系统进行说明的入口点;
file结构,主要用于与文件系统对应的设备驱动程序。
代表一个打开的文件,它由内核在open时创建,并传递给在该文件上进行操作的所有函数,直到碰到最后的close函数。
在文件的所有实例都被关闭之后,内核会释放这个数据结构;
inode结构,提供了关于特殊设备文件/dev/mydev的信息。
使用cat/proc/kmsg&来监测Kernel中的输出信息,即程序中的printk的输出信息。
同时通过#include
各个结构的定义如下:
(1)file_operations结构:
staticconststructfile_operationsmy_fops={
.owner=THIS_MODULE,
.llseek=my_llseek,
.read=my_read,
.write=my_write,
.open=my_open,
.release=my_release,
.unlocked_ioctl=ioctl,
};
(2)file结构:
1)读
staticssize_tmy_read(structfile*filp,char__user*buf,size_tsize,loff_t*ppos)
2)写
staticssize_tmy_write(structfile*filp,constchar__user*buf,size_tsize,loff_t*ppos)
3)seek文件定位
staticloff_tmy_llseek(structfile*filp,loff_toffset,intwhence)
4)I\O控制
staticintioctl(structfile*file,unsignedintcmd,unsignedlongarg)
(3)inode结构:
1)打开
intmy_open(structinode*inode,structfile*filp)
2)释放
intmy_release(structinode*inode,structfile*filp)
(4)信号量定义:
1)定义信号量
staticstructsemaphoresem;
2)初始化信号量为1
sema_init(&sem,1);
3)获取信号量
up(&sem)
4)释放信号量
down(&sem)
(5)自旋锁定义:
1)获得自旋锁
spin_lock(&spin);
2)释放自旋锁
spin_unlock(&spin);
(6)模块初始化和退出
1)模块初始化
module_init(mydev_init);
2)模块退出
module_exit(mydev_exit);
2.4算法流程图如下:
4、系统调试与分析
4.1使用su用户
4.2对源程序进行编译
4.3打开后台内核输出监控
4.4加载驱动程序并查看
4.5显示主设
4.6创建节点并查看
4.7编译测试程序
4.8运行测试程序
4.9打开设备
4.11启动另一个终端,打开设备
4.10读设备(创建设备的时候已经在缓存区设置数据)
4.11写入数据
4.12继续读数据(MAX_BUFF是20)
4.13释放设备
4.14退出测试程序
5、程序清单
mydev.c:
#include
#include
#include
#include
#include
#include
#include
#defineDEFAULT_MSG"Hello,WelcometoOScoursedesign"/*默认字符设备数据*/
#defineDEVICE_NAME"mydev"/*设备名*/
#defineMAXBUF100/*设备数据缓冲区大小*/
staticunsignedcharmydev_buf[MAXBUF];/*设备内存数据缓冲区*/
staticstructsemaphoresem;/*定义互斥信号量*/
staticintglobalvar_count=0;/*定义设备计数*/
//staticspinlock_tspin=SPIN_LOCK_UNLOCKED;#SPIN_LOCK_UNLOCKEDhasbeendeprecatedsince2.6.19
staticDEFINE_SPINLOCK(spin);/*定义自旋锁*/
/*定义读写释放打开*/
staticintmydev_open(structinode*inode,structfile*file);
staticintmydev_release(structinode*inode,structfile*file);
staticssize_tmydev_read(structfile*file,char__user*buf,size_tcount,loff_t*pos);
staticssize_tmydev_write(structfile*file,constchar__user*buf,size_tcount,loff_t*pos);
staticintmydev_open(structinode*inode,structfile*file){
//获得自选锁
spin_lock(&spin);
//临界资源访问
if(globalvar_count){
spin_unlock(&spin);
return-EBUSY;
}
globalvar_count++;
//释放自选锁
spin_unlock(&spin);
return0;
}
staticintmydev_release(structinode*inode,structfile*file){
globalvar_count--;
printk("设备资源释放!
\n");
return0;
}
staticssize_tmydev_read(structfile*file,char__user*buf,size_tcount,loff_t*pos){/*从设备读取count个数据到用户数据区buf中*/
intsize=count count: MAXBUF;/*检测读取的数据大小count是否比设备数据缓冲区大,如何大则截取MAXBUF的大小*/ printk("mydev: Thisismydevice! \n"); /*把设备内存mydev_buf中的数据拷贝到用户空间buf中,数量为size*/ if(copy_to_user(buf,mydev_buf,size)){ up(&sem); return-ENOMEM;/*内存不足错误*/ } up(&sem); returnsize; } staticssize_tmydev_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*pos){/*把buf中count个数据写入设备内存空间中*/ intsize=count count: MAXBUF;/*检测写入的数据大小count是否比设备数据缓冲区大*/ //获得信号量 if(down_interruptible(&sem)){ return-ERESTARTSYS; } printk("mydev: Thisismydevice! \n"); memset(mydev_buf,0,sizeof(mydev_buf));/*将设备内存清空*/ /*把buf中的用户数据写入到设备内存mydev_buf中,数量为size*/ if(copy_from_user(mydev_buf,buf,size)){ up(&sem); return-ENOMEM; } up(&sem); returnsize; } staticstructfile_operationsmydev_fops={ .read=mydev_read, .write=mydev_write, .open=mydev_open, .release=mydev_release, }; staticstructcdev*mydev_cdev;/*新设备指针*/ staticint__initmydev_init(void){/*模块初始化*/ dev_tdev;/*设备号*/ interror; error=alloc_chrdev_region(&dev,0,2,DEVICE_NAME);/*动态分配一个设备号*/ if(error){/*返回值不为0表示分配失败*/ printk("动态分配设备号失败! \n"); returnerror; } mydev_cdev=cdev_alloc();/*新分配一个字符设备对象*/ if(mydev_cdev==NULL){ printk("动态分配字符设备对象失败! \n"); unregister_chrdev_region(dev,2);/*注销一个分配的设备号区域*/ return-ENOMEM; } mydev_cdev->ops=&mydev_fops;/*设定字符设备操作函数指针*/ mydev_cdev->owner=THIS_MODULE;/*设备的属主*/ error=cdev_add(mydev_cdev,dev,1);/*将设备添加到内核中去*/ if(error){ printk("设备添加失败! \n"); unregister_chrdev_region(dev,2);/*注销一个分配的设备号区域*/ cdev_del(mydev_cdev);/*删除字符设备对象*/ returnerror; } memset(mydev_buf,0,sizeof(mydev_buf));/*清空设备缓冲区数据*/ memcpy(mydev_buf,DEFAULT_MSG,sizeof(DEFAULT_MSG));/*设定设备缓冲区默认数据*/ printk("设备添加成功,设备缓冲区默认数据: Hello,WelcometoOScoursedesign! \n"); sema_init(&sem,1); return0; } staticvoid__exitmydev_exit(void){/*模块卸载*/ unregister_chrdev_region(mydev_cdev->dev,2); cdev_del(mydev_cdev); printk("设备删除成功! \n"); } module_init(mydev_init); module_exit(mydev_exit); MODULE_LICENSE("GPL"); test.c: #include #include #include #include #defineMAXBUF20 intmain(){ inttestdev; inti; intlen; intt; charsel; intflag; charbuf[MAXBUF],tmp[MAXBUF]; printf("1、打开设备\n2、写操作\n3、读操作\n4、释放设备\n5、退出\n"); while (1){ printf("请输入要执行的操作: "); sel=getchar(); getchar(); switch(sel){ case'1': testdev=open("/dev/mydev",O_RDWR); if(testdev<0){ printf("设备打开失败\n"); break; } flag=0; printf("设备打开成功! \n"); break; case'2': if(flag){ printf("请先打开设备! \n"); continue; } printf("请输入要写入的字符串: "); gets(tmp); len=sizeof(tmp);//strlen(tmp); t=write(testdev,tmp,len); if(t<0){ perror("写操作失败! \n"); exit(-1); } printf("字符串: %s写入成功! \n",tmp); break; case'3': if(flag){ printf("请先打开设备! \n"); continue; } lseek(testdev,0,SEEK_SET); t=read(testdev,buf,MAXBUF); if(t<0){ perror("读操作失败! \n"); exit(-1); } printf("读操作成功! 结果为: %s\n",buf); break; case'4': if(flag){ printf("请先打开设备! \n"); break; } //release(testdev); close(testdev); printf("设备释放成功! \n"); flag=1; break; case'5': close(testdev); exit(0); default: printf("输入有误! \n"); break; } } } makefile: DEBFLAGS=-O2 EXTRA_CFLAGS+=$(DEBFLAGS) #CFLAGS+=-I$(LDDINC) ifneq($(KERNELRELEASE),) #callfromkernelbuildsystem xbrige-objs: =mydev.o obj-m: =mydev.o else KERNELDIR? =/lib/modules/$(shelluname-r)/build PWD: =$(shellpwd) modules: $(MAKE)-C$(KERNELDIR)M=$(PWD)modules #$(MAKE)-C$(KERNELDIR)M=$(PWD)LDDINC=$(PWD)/../includemodules endif clean: rm-rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versions depend.dependdep: $(CC)$(CFLAGS)$(EXTRA_CFLAGS)-M*.c>.depend ifeq(.depend,$(wildcard.depend)) include.depend endif 6、课设总结 在这次课程设计之前就一直在使用linux系统,在笔记本电脑上使用的是linuxmint15,所以在进行本次课程设计的时候选题为第二个会比较容易搭建环境。 但是使用linux也主要是在应用层面进行一些编程和娱乐活动,并没有涉及到驱动或者内核层次的hack,所以在本次课程设计中选择驱动开发也可以锻炼我相关的能力 驱动相较于Linux系统是更加熟悉的一个名词,每次重装系统都要安装各种各样的驱动,不然计算机就不能正常运行,各个硬件就不能发挥作用,通过这次课程设计,对Linux系统的驱动有了比较深入的认识: 。 Li
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 操作系统 课程设计 报告
![提示](https://static.bingdoc.com/images/bang_tan.gif)