ImageVerifierCode 换一换
格式:DOCX , 页数:37 ,大小:427.90KB ,
资源ID:16472875      下载积分:5 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bingdoc.com/d-16472875.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(第六章 设备管理实验.docx)为本站会员(b****6)主动上传,冰点文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰点文库(发送邮件至service@bingdoc.com或直接QQ联系客服),我们立即给予删除!

第六章 设备管理实验.docx

1、第六章 设备管理实验第六章 设备管理实验 【实验目的】 通过本实验的学习,了解Linux操作系统中的设备驱动程序包括哪些组成部分,并能编写简单的字符设备(scull,Simple Character Utility for Loading Localities)和块设备(sbull,Simple Block Utility for Loading Localities)的驱动程序以及对所编写设备驱动程序的测试,最终了解Linux操作系统是如何管理设备的。 【准备知识】 一设备驱动程序的简单介绍 Linux设备驱动程序集成在内核中,实际上是处理或操作硬件控制器的软件。从本质上讲,驱动程序是常驻内

2、存的低级硬件处理程序的共享库,设备驱动程序就是对设备的抽象处理;也即是说,设备驱动程序是内核中具有高特权级的、常驻内存的、可共享的下层硬件处理例程。 设备驱动程序软件封装了如何控制这些设备的技术细节,并通过特定的接口导出一个规范的操作集合(见图1);内核使用规范的设备接口(字符设备接口和块设备接口)通过文件系统接口把设备操作导出到用户空间程序中。(由于本实验不涉及网络设备,故在此就不作讨论) 在Linux中,字符设备和块设备的I/O操作是有区别的。块设备在每次硬件操作时把多个字节传送到主存缓存中或从主存缓存中把多个字节信息传送到设备中;而字符设备并不使用缓存,信息传送是一个字节一个字节地进行的

3、。 Linux操作系统允许设备驱动程序作为可装载内核模块实现,这也就是说,设备的接口实现不仅可以在Linux 操作系统启动时进行注册,而且还可以在Linux 操作系统启动后装载模块时进行注册。 总之,Linux操作系统支持多种设备,这些设备的驱动程序有如下一些特点: (1)内核代码:设备驱动程序是内核的一部分,如果驱动程序出错,则可能导致系统崩溃。 (2)内核接口:设备驱动程序必须为内核或者其子系统提供一个标准接口。比如,一个终端驱动程序必须为内核提供一个文件I/O接口;一个SCSI设备驱动程序应该为SCSI子系统提供一个SCSI设备接口,同时SCSI子系统也必须为内核提供文件的I/O接口及缓

4、冲区。 (3)内核机制和服务:设备驱动程序使用一些标准的内核服务,如内存分配等。 (4)可装载:大多数的Linux操作系统设备驱动程序都可以在需要时装载进内核,在不需要时从内核中卸载。 (5)可设置:Linux操作系统设备驱动程序可以集成为内核的一部分,并可以根据需要把其中的某一部分集成到内核中,这只需要在系统编译时进行相应的设置即可。 (6)动态性:当系统启动且各个设备驱动程序初始化后,驱动程序将维护其控制的设备。如果该设备驱动程序控制的设备不存在也不影响系统的运行,此时的设备驱动程序只是多占用了一点系统内存罢了。 二设备驱动程序与外界的接口 每种类型的驱动程序,不管是字符设备还是块设备都为

5、内核提供相同的调用接口,故内核能以相同的方式处理不同的设备。Linux为每种不同类型的设备驱动程序维护相应的数据结构,以便定义统一的接口并实现驱动程序的可装载性和动态性。 Linux设备驱动程序与外界的接口可以分为如下三个部分: (1)驱动程序与操作系统内核的接口:这是通过数据结构file_operations来完成的。 (2)驱动程序与系统引导的接口:这部分利用驱动程序对设备进行初始化。 (3)驱动程序与设备的接口:这部分描述了驱动程序如何与设备进行交互,这与具体设备密切相关。 可归结为如下图2: 三设备驱动程序的组织结构 设备驱动程序有一个比较标准的组织结构,一般可以分为下面三个主要组成部

6、分: (1)自动配置和初始化子程序 这部分程序负责检测所要驱动的硬件设备是否存在以及是否能正常工作。如果该设备正常,则对设备及其驱动程序所需要的相关软件状态进行初始化。这部分程序仅在初始化时被调用一次。 (2)服务于I/O请求的子程序 该部分又可称为驱动程序的上半部分。系统调用对这部分进行调用。系统认为这部分程序在执行时和进行调用的进程属于同一个进程,只是由用户态变成了内核态,而且具有进行此系统调用的用户程序的运行环境。故可以在其中调用与进程运行环境有关的函数。 (3)中断服务子程序 该部分又可称为驱动程序的下半部分。设备在I/O请求结束时或其它状态改变时产生中断。中断可以产生在任何一个进程运

7、行时,因此中断服务子程序被调用时不能依赖于任何进程的状态,因而也就不能调用与进程运行环境有关的函数。因为设备驱动程序一般支持同一类型的若干设备,所以一般在系统调用中断服务子程序时都带有一个或多个参数,以唯一标识请求服务的设备。 四设备驱动程序的代码 设备驱动程序是一些函数和数据结构的集合,这些函数和数据结构是为实现管理设备的一个简单接口。操作系统内核使用这个接口来请求驱动程序对设备进行I/O操作。甚至,我们可以把设备驱动程序看成是一个抽象数据类型,它为计算机中的每个硬件设备都建立了一个通用函数接口。由于一个设备驱动程序就是一个模块,所以在内核内部用一个file结构来识别设备驱动程序,而且内核使

8、用file_operatuions结构来访问设备驱动程序中的函数。 了解设备驱动程序代码的如下几个部分: 驱动程序的注册与注销。 设备的打开与释放。 设备的读写操作。 设备的控制操作。 设备的中断和轮询处理。 第一部分 字符设备驱动程序的代码 1 了解什么是字符设备 2 了解字符设备的基本入口点 字符设备的基本入口点也可称为子程序,它们被包含在驱动程序的file_operations结构中。 open()函数; release()函数; read()函数; write()函数; ioctl()函数; select()函数。 3 字符设备的注册 设备驱动程序提供的入口点在设备驱动程序初始化时向系

9、统登记,以便系统调用。Linux系统通过调用register_chrdev()向系统注册字符型设备驱动程序。 register_chrdev()定义如下: #include #include int register_chrdev(unsigned int major, const char *name, struct file_operations *ops); 其中major时设备驱动程序向系统申请的主设备号。如果它为0,则系统为该驱动程序动态地分配第一个空闲的主设备号,并把设备名和文件操作表的指针置于chrdevs表的相应位置。name是设备名,ops是对各个调用入口点的说明。regis

10、ter_chrdev()函数返回0表示注册成功;返回-EINVAL表示申请的主设备号非法,一般主设备号大于系统所允许的最大设备号;返回-EBUSY表示所申请的主设备号正被其它设备驱动程序使用。如果动态分配主设备号成功,则该函数将返回所分配的主设备号。如果register_chrdev()操作成功,则设备名就会出现在/proc/devices文件中。 字符设备注册以后,还必须在文件系统中为其创建一个代表节点。该节点可以是在/dev目录中的一个节点,这种节点都是文件节点,且每个节点代表一个具体的设备。不过要有主设备号和从设备号两个参数才能创建一个节点。还可以是在devfs设备文件目录下的一个节点,

11、对于这种节点应根据主设备号给每一种设备都创建一个目录节点,在这个目录下才是代表具体设备的文件节点。 【实验内容】 编写一个简单的字符设备驱动程序。要求该字符设备包括scull_open()、scull_write()、scull_read()、scull_ioctl()和scull_release()五个基本操作,并编写一个测试程序来测试你所编写的字符设备驱动程序。 【实验指导】 先给出字符设备驱动程序要用到的数据结构定义: struct device_struct const char *name; struct file_operations *chops; ; static struct

12、 device_struct chrdevsMAX_CHRDEV; typedef struct Scull_Dev void *data; int quantum; / the current quantum size int qset; / the current array size unsigned long size; unsigned int access_key; / used by sculluid and scullpriv unsigned int usage; / lock the device while using it struct Scull_Dev *next;

13、 / next listitem scull; 1 字符设备的结构 字符设备的结构即字符设备的开关表。当字符设备注册到内核后,字符设备的名字和相关操作被添加到device_struct结构类型的chrdevs全局数组中,称chrdevs为字符设备的开关表。下面以一个简单的例子说明字符设备驱动程序中字符设备结构的定义:(假设设备名为scull) * file_operation结构定义如下,即定义chr设备的_fops * static int scull_open(struct inode *inode,struct file *filp); static int scull_release(

14、struct inode *inode,struct file *filp); static ssize_t scull_write(struct inode *inode,struct file *filp,const char *buffer,int count); static ssize_t scull_read(struct inode *inode,struct file *filp,char *buffer,int count); static int scull_ioctl(struct inode *inode,struct file *filp,unsigned long

15、int cmd,unsigned long arg); struct file_operation chr_fops = NULL, / seek scull_read, / read scull_write, / write NULL, / readdir NULL, / poll scull_ioctl, / ioctl NULL, / mmap scull_open, / open NULL, / flush scull_release, / release NULL, / fsync NULL, / fasync NULL, / check media change NULL, / r

16、evalidate NULL / lock ; 2 字符设备驱动程序入口点 字符设备驱动程序入口点主要包括初始化字符设备、字符设备的I/O调用和中断。在引导系统时,每个设备驱动程序通过其内部的初始化函数init()对其控制的设备及其自身初始化。字符设备初始化函数为chr_dev_init(),包含在/linux/drivers/char/mem.c中,它的主要功能之一是在内核中登记设备驱动程序。具体调用是通过register_chrdev()函数。register_chrdev()函数定义如下:#include #include int register_chrdev(unsigned int

17、 major,const char *name,struct file_operation *fops); 其中major是为设备驱动程序向系统申请的主设备号。如果为0,则系统为此驱动程序动态地分配一个主设备号。name是设备名。fops是前面定义的file_operation结构的指针。在登记成功的情况下,如果指定了major,则register_chrdev()函数返回值为0;如果major值为0,则返回内核分配的主设备号。并且register_chrdev()函数操作成功,设备名就会出现在/proc/devices文件里;在登记失败的情况下,register_chrdev()函数返回值为

18、负。 初始化部分一般还负责给设备驱动程序申请系统资源,包括内存、中断、时钟、I/O端口等,这些资源也可以在open()子程序或别的地方申请。在这些资源不用的时候,应该释放它们,以利于资源的共享。 用于字符设备的I/O调用主要有:open()、release()、read()、write()和ioctl()。open()函数的使用比较简单,当一个设备被进程打开时,open()函数被唤醒:static int scull_open(struct inode *inode,struct file *filp) MOD_INC_USE_COUNT; return 0;注意宏MOD_INC_USE_CO

19、UNT的使用: Linux内核需要跟踪系统中每个模块的使用信息,以确保设备的安全使用。MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT可以检查使用驱动程序的用户数,以保护模块被意外地卸载。release()函数的使用和open()函数相似:static int scull_release(struct inode *inode,struct file *filp) MOD_DEC_USE_COUNT; return 0;当设备文件执行read()调用时,将从设备中读取数据,实际上是从内核数据队列中读取,并传送给用户空间。设备驱动程序的write()函数的使用和read()

20、函数相似,只不过是数据传送的方向发生了变化,即按要求的字节数count从用户空间的缓冲区buf复制到硬件或内核的缓冲区中。 有时需要获取或改变正在运行的设备的参数,这时就需要用到ioctl()函数: static int scull_ioctl(struct inode *inode,struct file *filp,unsigned long int cmd,unsigned long arg);其中参数cmd是驱动程序要执行的命令的特殊代码;参数arg是任何类型的4字节数,它为特定的cmd提供参数。在Linux中,内核中的每个设备都有唯一的基本号(base number)及和基本号相关的

21、命令范围。具体的ioctl基本号可参见Documentation/ioctlnumber。Linux中定义了四种ioctl()函数调用:_IO(base,command) / 可以定义所需要的命令,没有数据传送的问题,返回正数 _IOR(base,command,size) / 读操作的ioctl控制 _IOW(base,command,size) / 写操作的ioctl控制 _IOWR(base,command,size) / 读写操作的ioctl控制 当用到的硬件设备能产生中断信号时,需要中断服务子程序。 下面给出几个入口函数流程图的参考设计。 2.1 函数scull_open() 2.2

22、 函数scull_write() 2.3 函数scull_read() 2.4 函数scull_ioctl() 2.5 函数scull_release() 3 字符设备驱动程序的安装 编写完设备驱动程序后,下一项任务是对它进行编译和装入可引导的内核。对字符驱动程序,可以用下面的步骤来完成:(2)在chr_dev_init()函数的最后增加调用init_module()子程序的行(chr_dev_init()函数在drivers/char/mem.c中)。(3)编辑drivers/char目录中的makefile,将driver.o的名称放在OBJS定义的后面,并将driver.c名称放在SRC

23、S定义的后面。(4)将目录改变到Linux源程序目录的最上层,重新建立和安装内核。作为一般性预防措施,当改变内核的代码时,应当将计算机上重要的内容作一次备份。(5)如果用lilo引导系统,最好将新内核作为试验项,在lilo.conf文件中另加一个Linux引导段。4 测试函数 在该字符设备驱动程序编译加载后,再在/dev目录下创建字符设备文件chrdev,使用命令: #mknod /dev/chrdev c major minor ,其中“c”表示chrdev是字符设备,“major”是chrdev的主设备号。 (该字符设备驱动程序编译加载后,可在/proc/devices文件中获得主设备号,

24、或者使用命令: #cat /proc/devices | awk ”$2= ”chrdev” print$1” 获得主设备号)。该测试函数的流程图可如下设计: 第二部分 块设备驱动程序的代码 1 了解什么是块设备 2 块设备驱动程序描述符 块设备驱动程序描述符是一个blk_dev_struct类型的数据结构,器定义如下: struct blk_dev_struct / queue_proc has to be atomic request_queue_t request_queue ; queue_proc *queue ; void *date ; ; 在这个结构中,请求队列request_

25、queue是主体。对于函数指针queue ,当其为非0时,就调用这个函数来找到具体设备的请求队列,这是为考虑具有同一主设备号的多种同类设备而设的一个域,该指针也在初始化时就设置好。还有一个指针data是辅助queue函数找到特定设备的请求队列。 所有块设备的描述符都存放在blk_dev表中:struct blk_dev_struct blk_devMAX_BLKDEV;每个块设备都对应着数组中的一项,可以用主设备号进行检索。每当用户进程对一个块设备发出一个读写请求时,首先调用块设备所公用的函数generic_file_read()和generic_file_write()。如果数据存在在缓冲区

26、中或缓冲区还可以存放数据,就同缓冲区进行数据交换。否则,系统会将相应的请求队列结构添加到其 对应项的blk_dev_struct中,如下图所示: 如果在加入请求队列结构时该设备没有请求,则马上响应该请求,否则将其追加到请求任务队列尾顺序执行。 3 了解块设备的基本入口点 读写函数; request()函数(处理请求函数); ioctl函数; check_media_change函数; revalidate函数。 4 块设备的注册 和字符设备驱动程序类似,内核里的块设备驱动程序也是由一个主设备号来标识。不过,对于块设备驱动程序的注册不仅在其初始化的时候进行而且在编译的时候也要进行注册。在初始化时

27、通过register_blkdev()函数将相应的块设备添加到数组blkdevs中,该数组在fs/block_dev.c中定义如下: static struct const char *name; struct block_device_operations *bdops; blkdevsMAX_BLKDEV; 对块设备驱动程序注册的调用格式为: int register_blkdev(unsigned int major,const char *name,struct block_device_operations *bdops); 其中第一个参数是主设备号;第二个参数是设备名;第三个参数是

28、指向具体块设备操作的指针。如果一切顺利则返回0,否则返回负值。如果指定的主设备号为0,则该函数会搜索空闲的主设备号分配给该块设备驱动程序并将其作为返回值。 块设备驱动程序的注册示意图: 【实验内容】 编写一个简单的块设备驱动程序。要求该块设备包括sbull_open()、sbull_ioctl()和sbull_release()等基本操作。 【实验指导】 由于块设备驱动程序的绝大部分都是与设备无关的,故内核的开发者通过把大部分相同的代码放在一个头文件中来简化驱动程序的代码。从而每个块设备驱动程序都必须包含这个头文件。先给出块设备驱动程序要用到的数据结构定义: struct device_str

29、uct const char *name; struct file_operations *chops; ; static struct device_struct blkdevsMAX_BLKDEV; struct sbull_dev void *data; int quantum; / the current quantum size int qset; / the current array size unsigned long size; unsigned int access_key; / used by sbulluid and sbullpriv unsigned int usa

30、ge; / lock the device while using it unsigned int new_msg; struct sbull_dev *next; / next listitem ; extern struct sbull_dev *sbull; / device information 1 块设备的结构 块设备的结构即块设备的开关表。当块设备注册到内核后,块设备的名字和相关操作被添加到device_struct结构类型的blkdevs全局数组中,称blkdevs为块设备的开关表。下面以一个简单的例子说明块设备驱动程序中块设备结构的定义:(假设设备名为sbull) * file_operation结构定义如下,即定义sbull设备的_fops * struct file_operation blk_fops = NULL, / seek block_read, / 内核函数 block_write, / 内核函数 NULL, / readdir NULL, / p

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2