嵌入式系统驱动程序开发_第1页
嵌入式系统驱动程序开发_第2页
嵌入式系统驱动程序开发_第3页
嵌入式系统驱动程序开发_第4页
嵌入式系统驱动程序开发_第5页
已阅读5页,还剩81页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第5章嵌入式系统驱动程序开发

1主要内容模块编程编译模块进内核嵌入式系统驱动程序概述嵌入式系统驱动程序结构LED灯驱动程序实例2模块编程内核模块是Linux内核向外部提供的一个插口,其全称为动态可加载内核模块〔LoadableKernelModule,LKM〕。Linux本身是一个单内核,单内核的最大优点是效率高,因为所有的内容都集成在一起,但其缺点是可扩展性和可维护性相对较差,模块机制就是为了弥补这一缺陷。3模块编程模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。模块在运行时被链接到内核作为内核的一局部在内核空间运行,这与运行在用户空间的进程是不同的。模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其它内核上层的功能。4将模块从内核中独立出来,不必预先绑在kernelcodes中。这样做有三种优点:1〕将来修改内核时,不必全部重新编译,可节省不少时间;2〕假设需要安装新的模块,不必重新编译内核,只要插入〔通过insmode指令〕对应的模块即可;3〕减少内核对系统资源的占用,内核可以集中精力做最根本的事情,把一些扩展功能都交由模块实现。

5模块编程模块命令insmod挂载模块insmodhello.ko将hello.ko模块插入到内核中rmmod移除已挂载模块rmmodhello.ko将hello.ko模块从内核中卸载lsmod列出已经加载的内核模块modinfo查看模块信息modinfohello.ko查看hello.ko模块信息

modprobe自动根据依赖文件装入模块dmesg查看log信息6modprobe功能说明:自动处理可载入模块。语法:modprobe[-acdlrtvV][--help][模块文件][符号名称=符号值]补充说明:modprobe可载入指定的个别模块,或是载入一组相依的模块。modprobe会根据depmod所产生的相依关系,决定要载入哪些模块。假设在载入过程中发生错误,在modprobe会卸载整组的模块。7modprobe参数:

-a或--all载入全部的模块。

-c或--show-conf显示所有模块的设置信息。

-d或--debug使用排错模式。

-k或--autoclean指定模块设置为"自动去除"模式。

-l或--list显示可用的模块。

-n或--show

仅仅显示要执行的操作,而不实际执行

-q或--quiet不显示错误信息。8modprobe参数:

-r或--remove模块闲置不用时,即自动卸载模块。

-s或--syslog//将结果记录到系统记录中。

-t或--type指定模块类型。

-v或--verbose执行时显示详细的信息。

-V或--version显示版本信息。

-C或--configconfigfile指定配置文件,默认使用/etc/modules.conf文件为配置文件。

-h或-help显示帮助。9insmod挂载模块insmod这个工具,和modprobe有点类似,但功能上没有modprobe强,modprobe在挂载模块是不用指定模块文件的路径,也不用带文件的后缀.o或.ko;而insmod需要的是模块的所在目录的绝对路径,并且一定要带有模块文件名后缀的(modulefile.o或modulesfile.ko〕10模块编程一个linux内核模块主要由以下几个局部组成模块加载函数(必须)模块卸载函数(必须)模块许可证声明(必须)模块参数(可选)模块导出符号(可选)模块作者等信息声明(可选)11模块编程模块加载函数当通过insmod命令加载内核模块时,模块的加载函数会自动被内核执行,完本钱模块的相关初始化工作。linux模块加载函数的形式如下:

staticintinitialization_function(void)

{

/*初始化代码*/

}模块加载函数必须以module_init(initialization_function)的形式被指定。它返回整型值,假设初始化成功,返回0,否那么返回错误编码。当通过insmod和modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完本钱模块的相关初始化工作。12模块编程模块卸载函数当通过rmmod命令卸载内核模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。linux内核模块卸载函数的形式如下:

staticvoid

cleanup_function(void)

{

/*释放代码*/

}模块卸载函数在模块卸载的时候执行,不返回任何值,必须以module_exit(cleanup_function)的形式来指定;模块卸载函数在模块卸载的时候执行,不返回任何值,必须以“module_exit(函数名)〞的形式来指定。13模块编程通常来说,模块卸载函数要完成与模块加载函数相反的功能:假设模块加载函数注册了XXX,那么模块卸载函数应该注销XXX;假设模块记载函数的动态申请了内存,那么模块函数应该释放该该内存。假设模块加载函数申请了硬件资源(中断,DMA通道、I/O端口和I/O内存等)的占用,那么模块卸载函数应该释放这些硬件资源。模块加载函数一般用来开启硬件,模块卸载函数一般要关闭硬件。14模块编程模块许可证声明MODULE_LICENSE("DaulBSD/GPL")模块许可证(LICENSE)声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染(kerneltainted)的警告。在linux2.6内核中,可接受的LICENSE包括“GPL〞、“GPLv2〞、“GPLandadditionalrights〞、“DualBSD/GPL〞、“DualMPL/GPL〞和“Proprietary“。大多数情况下,内核模块应遵循GPL兼容许可权。linux2.6内核模块中最常见的是以MODULE_LICENSE(“DualBSD/GPL〞)语句声明模块采用BSD/GPL双LICENSE。15模块编程模块导出符号Linux2.6的“/proc/kallsyms〞文件对应着内核符号表,它记录了符号以及符号所在的内存地址。内核模块可以使用如下宏导出符号(symbol,对应于函数或变量)到内核符号表。EXPORT_SYMBOL(符号名);EXPORT_SYMBOL_GPL(符号名);//只适用于包含GPL许可权的模块。导出的符号将可以被其它模块使用,使用前声明一下即可以。

16模块编程模块作者等信息声明MODULE_AUTOR("作者信息");MODULE_DESCRIPTION("模块描述信息");MODULE_VERSION("版本信息");MODULE_ALIAS("别名信息");MODULE_DEVICE_TABLE("设备表信息");17模块编程介绍printk()printk是在内核中运行的向控制台输出显示的函数。模块运行在内核空间,不能依赖于标准C库的。标准C库需要调用操作系统内核提供的系统调用来完成打印字符的工作。18printk有8个级别,定义在<include/linux/kernel.h>中:#defineKERN_EMERG“<0>〞/*紧急事件消息,系统崩溃之前提示,表示系统不可用*/#defineKERN_ALERT“<1>〞/*报告消息,表示必须立即采取措施*/#defineKERN_CRIT“<2>〞/*临界条件,通常涉及严重的硬件或软件操作失败*/#defineKERN_ERR“<3>〞/*错误条件,驱动程序常用KERN_ERR来报告硬件的错误*/19printk有8个级别,定义在<include/linux/kernel.h>中:#defineKERN_WARNING“<4>〞/*警告条件,对可能出现问题的情况进行警告*/#defineKERN_NOTICE“<5>〞/*正常但又重要的条件,用于提醒*/#defineKERN_INFO“<6>〞/*提示信息,如驱动程序启动时,打印硬件信息*/#defineKERN_DEBUG“<7>〞/*调试级别的消息*/20模块编程通过〞insmodhello.ko〞命令可以加载它,加载时输出〞helloworld〞。通过〞rmmodhello〞命令可以卸载它,卸载时输出〞hellomoduleexit〞,查看输出信息可通过dmesg命令。21编写一个hello.c模块:1〕编写代码:#include<linux/init.h>#include<linux/module.h>/*Neededbyallmodules*/MODULE_LICENSE(“DualBSD/GPL〞);staticinthello_init(void){printk(KERN_INFO“Helloworldenter\n〞);/*内核中打印信息要用printk(),用户中用printf()。printk()可定义输出级别*/return0;}staticvoidhello_exit(void){printk(KERN_INFO“HelloWorldexit\n");}module_init(hello_init);/*加载模块。假设成功,返回0。失败时,应输出错误编码*/module_exit(hello_exit);//卸载模块,不返回任何值。MODULE_DESCRIPTION(“AsimpleHelloWorldModule〞);MODULE_ALIAS(“asimplestmodule〞);22加载模块失败时应输出错误编码,在Linux内核中,错误编码是一个负值。在<linux/errno.h>中定义,包含-ENODEV、-ENOMEM之类的符号值。在2.6内核中,可用request_module(constchar*fmt,…)函数加载内核模块。23模块编程实例1-1:用命令在X86下加载和卸载hello模块在同一目录下编辑Makefile文件。添加这一行:obj-m:=hello.o编译模块:

#make-C/lib/modules/2.6.32-21-generic/buildM=$(pwd)modules编译产生hello.ko目标文件加载hello命令:#sudoinsmod./hello.ko用rmmod可以卸载模块#sudormmodhello通过lsmod命令可以获得系统中加载的所有模块以及模块间的依赖关系。用命令dmesg来查看log信息。24模块编程实例1-1说明obj-m:=hello.oobj-m表示要由hello.c文件编译得到hello.o,并作为模块编译,obj-y表示要连接进内核,obj-x表示不会被编译是内核模块的安装路径,就是编译内核的时候“make

modules_install〞在/lib/modules/目录下生成的系统源码包;M=后面的是指存放hello.c和Makefile所在的目录。25模块编程实例1-2:编译arm版本的hello模块#makeclean清理中间文件和目标文件。编译arm下的模块:#make-C../../workhome/ces-6410/uboot/htx-linux-2.6.24-yaffs2M=$(pwd)modules其中是目标板的linux内核源码目录。注:编译过的的arm内核是目标板的内核,在编译时只能用这个内核进行编译。加载模块:将目标文件hello.ko放到目标板上的下,再加载:#insmodhello.ko查看模块用lsmod命令;用rmmod命令卸载模块:#rmmodhello26模块编程模块参数模块参数是模块被加载的时候可以被传递给模块的值,它本身对应模块内部的全局变量。可以使用“module_param(参数名,参数类型,读/写权限)〞为模块定义一个参数。27模块参数例如:以下代码定义了一个整型参数和一个字符指针参数。

staticchar*book_name="linux设备驱动";

staticintnum=4000;

module_param(num,int,S_IRUGO);

module_param(book_name,charp,S_IRGUO);在装载内核模块时,用户可以向内核模块传递参数,

形式为“sudoinsmod/modprobe模块名(例如linux.ko)

参数名=参数值〞,如果不传递,参数将使用模块内定义的默认值。28程序:hello_param.c#include<linux/module.h>//所有模块都需要的头文件

#include<linux/init.h>//init&exit相关宏

MODULE_LICENSE(“DualBSD/GPL");

staticchar*book_name=“dissectingLinuxDeviceDriver〞;staticintnum=4000;staticinthello_init(void)

{

printk("Hellomoduleinit\n");

return0;

}

staticvoidhello_exit(void)

{

printk("Hellomoduleexit\n");

}

module_init(hello_init);

module_exit(hello_exit);module_param(num,int,S_IRUGO);module_param(book_name,charp,S_IRUGO);29模块编程Makefile模块的编写需要配置过的内核源码,编译过程首先会到内核源码目录下,读取顶层Makefile文件,然后再返回模块所在的目录进行编译。30模块编程实例2:复杂点的Makefile文件Makefile内容ifneq($(KERNELRELEASE),)

obj-m:=hello.o

else

KDIR:=/lib/modules/$(shelluname-r)/build

PWD:=$(shellpwd)all:

$(MAKE)-C$(KDIR)M=$(PWD)

endifclean:

rm-f*.o*.ko*.mod.c.hello*编译:make31实例2:复杂点的Makefile文件KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,当第一次读取执行此makefile时,没有被定义,所以make将读取执行else之后的内容。如果make的目标是clean,直接执行clean操作,然后结束。如果make的目标是all,-C$(KDIR)指明跳转到内核源码目录下读取那里的makefile;M=$(PWD)说明返回到当前目录继续读入,执行当前的makefile。32模块编程模块依赖于Linux版本、CPU等因素查看可以下载的Linux内核源码包sudoapt-cachesearchlinux-source选定要下载的源码包sudo下载完成后,在/usr/src下,文件名为:linux-source-2.6.32.tar.bz2的压缩包33编译模块进内核2.6内核的源码树的目录下一般都会有两个文档Kconfig〔2.4版本是Config.in〕和Makefile。分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录源文件相关的内核配置菜单。在内核配置makemenuconfig〔或xconfig等〕时,从Kconfig中读出配置菜单,用户配置完成后保存到.config〔在顶层目录下生成〕中。在内核编译时,主Makefile调用这个.config,就知道了用户对内核的配置情况。34编译模块进内核Kconfig对应着内核的配置菜单,要想添加新的驱动到内核的源码中,可以通过修改Kconfig来增加对我们驱动的配置菜单,这样就有途径选择我们的驱动。假设想使这个驱动被编译,还要修改该驱动所在目录下的Makefile。因此,一般添加新的驱动时需要修改的文件有两种〔注意不只是两个〕。*Kconfig,*Makefile要想知道怎么修改这两种文件,就要知道这两种文件的语法结构。35〔1〕Kconfig每个菜单项都有一个关键字标识,最常见的就是config。语法: configsymbol optionssymbol就是新的菜单项,options是在这个新的菜单项下的属性和选项,其中options局部有:a.类型定义:每个config菜单项都要有类型定义,bool:布尔类型,tristate三态:内建、模块、移除,string:字符串,hex:十六进制,integer:整型。编译模块进内核36编译模块进内核〔1〕Kconfigoptions局部有:a.类型定义:例:configHELLO_MODULE

bool"hellotestmodule"bool类型的只能选中或不选中,显示为[];tristate类型的菜单项多了编译成内核模块的选项,显示为<>,假设选择编译成内核模块,那么会在.config中生成一个CONFIG_HELLO_MODULE=m的配置,假设选择内建,就是直接编译进内核,就会在.config中生成一个CONFIG_HELLO_MODULE=y的配置37〔1〕Kconfigoptions局部有:b.依赖型定义dependson或requires指此菜单的出现是否依赖于另一个定义:例如:configHELLO_MODULEbool“hellotestmodule〞 dependsonARCH_PXA这个例子说明HELLO_MODULE这个菜单项只对XScale处理器有效,即只有在选择了ARCH_PXA,该菜单才可见〔可配置〕。编译模块进内核38〔1〕Kconfigoptions局部有:c.帮助性定义:只是增加帮助用关键字help或help编译模块进内核39编译模块进内核〔1〕Kconfigmenu"Networkdevicesupport"

configNETDEVICES

bool"EnableNetDevices"

dependsonNET

defaulty

help

Thisishelpdesciption。

...endmenu

40编译模块进内核〔1〕Kconfig在menu/endmenu中的内容会成为Networkdevicesupport的子菜单。每一个子菜单项都是由config来定义的。config下方的那些bool、dependson、default、help等为config的属性,用于定义该菜单项的类型、依赖项、默认值、帮助信息等。

41编译模块进内核〔1〕Kconfig目录层次迭代:在上级Kconfig中有类似语句:source“drivers/usb/Kconfig“或source"drivers/misc/Kconfig“用来包含〔或嵌套〕新的Kconfig文件,这样便可以使各个目录管理各自的配置内容,不必把那些配置都写在同一个文件里,方便修改和管理。misc驱动是一些拥有着共同特性的简单字符设备驱动。所有的misc设备被分配同一个主设备号MISC_MAJOR(10),但是每一个可以选择一个单独的次设备号。42编译模块进内核〔2〕内核的Makefile内核的Makefile分为5个组成局部:

1〕Makefile最顶层的Makefile

2〕.config内核的当前配置文件,编译时成为顶层Makefile的一局部

3〕arch/$(ARCH)/Makefile和体系结构相关的Makefile

4〕s/Makefile.*一些Makefile的通用规那么

5〕kbuildMakefile各级目录下的大概约500个文件,编译时根据上层Makefile传下来的宏定义和其它编译规那么,将源代码编译成模块或编入内核。43编译模块进内核〔2〕Makefile顶层的Makefile文件读取.config文件的内容,并总体上负责build内核和模块。ArchMakefile那么提供补充体系结构相关的信息。s目录下的Makefile文件包含了所有用来根据kbuildMakefile构建内核所需的定义和规那么。44编译模块进内核〔2〕Makefileobj-$(CONFIG_HELLO)+=hello.o根据.config文件的CONFIG_变量来决定文件的编译方式〔编译进内核或编译成模块〕451〕进入内核,在driver/misc下建立hello文件夹,并编写一个名为hello.c的模块:#include<linux/init.h>#include<linux/module.h>MODULE_LICENSE(“DualBSD/GPL〞);staticinthello_init(void){printk(KERN_INFO“Helloworld!\n");return0;}staticvoidhello_exit(void){printk(KERN_INFO"Goodbyeworld!\n");}module_init(hello_init);module_exit(hello_exit);实例3:将模块编译进内核在arm开发板上运行462〕建立Kconfig在hello文件夹下建立Kconfig,内容如下: menu“HelloDriver〞 comment“HelloDriver〞 configHELLO tristate“Hellosupport〞 help thisisahellodiriverwhichcansay“helloworld!〞; endmenu473〕建立Makefile在hello文件夹下建立Makefile,内容如下:obj-$(CONFIG_HELLO)+=hello.o4〕使Kconfig和Makefile生效编辑driver/misc/Kconfig,在ifMISC_DEVICES下添加语句:sourcedrivers/misc/hello/Kconfig编辑driver/misc/Makefile,添加:obj-$(CONFIG_HELLO)+=hello.o/485〕到内核的顶层目录,执行sudomakemenuconfig,选中该驱动在Devicedriver->Micsdevices->HelloDriver->495〕到内核的顶层目录,执行sudomakemenuconfig,选中该驱动在Devicedriver->Micsdevices->HelloDriver->按h输出帮助信息按y,出现个*号,表示将其编译进内核M编译为模块506〕编译:#sudomake信息如下:516〕编译:编译结果如下:527〕把内核烧入教学平台启动信息如下:有helloworld信息输出,说明已经成功,hello驱动已在内核中注册。538〕把hello驱动编译成模块:在第5步中,把驱动选择改成以模块方式编译,选M,如下:548〕把hello驱动编译成模块:再来编译,看到它正以模块的方式被编译:558〕把hello驱动编译成模块:编译结果:569〕测试模块在教学平台启动进入文件系统后,把hello.ko拷到下,如果没有要事先建立。〔#mkdir〕进入目录,通过insmod进行挂载,rmmod进行卸载。到此,一个简单的hello驱动编译进内核和编译成模块的实验完成。57设备驱动最通俗的解释是“驱使硬件设备行动〞。设备驱动与底层硬件直接打交道,按照硬件设备的具体工作方式读写设备存放器,完成设备的轮询、中断、DMA通信,进行物理内存向虚拟内存的映射,最终使通信设备能够收发数据。设备驱动的一个根本特征是设备处理的抽象概念。嵌入式系统驱动程序概述58嵌入式系统驱动程序概述在Linux系统中,所有的外部设备都被看作是目录/dev下的一个文件,也就是系统把外部设备当作特殊文件来处理,并为外部设备提供一种标准接口,使得系统像访问文件一样访问外部设备。应用程序通过调用标准的设备文件操作函数来翻开、关闭、读取和写入设备。例如系统中的第一个IDE硬盘被表示成/dev/had。59嵌入式系统驱动程序概述Linux支持三类硬件设备:一类是块设备,类似磁盘以记录块或扇区为单位,成块进行输入/输出的设备;另一类是字符设备,类似键盘以字符为单位,逐个进行输入/输出的设备。第三类是网路设备是一种特殊设备;/dev下没有对应于网络设备的文件,正常的文件操作(read,write等)对于网络设备没有意义。网络设备可以通过套接口访问。60字符设备指那些无需缓冲直接读写的设备,字符设备接口只支持顺序存取的功能。块设备那么仅能以块为单位读写,典型的块大小为512或1024字节。块设备的存取是通过buffercache来进行并且可以随机访问,即不管块位于设备中何处都可以对其进行读写。块设备可以通过其设备相关文件进行访问,但更为平常的访问方法是通过文件系统。文件系统通常都建立在块设备上。网络设备可以通过套接口访问。嵌入式系统驱动程序概述61块〔磁盘〕设备和字符设备的设备相关文件可以通过mknod命令来创立,并使用主从设备号来描述此设备。网络设备也用设备相关文件来表示,但Linux寻找和初始化网络设备时才建立这种文件。由同一个设备驱动控制的所有设备具有相同的主设备号,从设备号那么被用来区分具有相同主设备号且由相同设备驱动控制的不同设备。嵌入式系统驱动程序概述62嵌入式系统驱动程序概述设备驱动程序实际是处理和操作硬件控制器的软件,从本质上讲,是内核中具有最高特权级的、驻留内存的、可共享的底层硬件处理例程。驱动程序是内核的一局部,是操作系统内核与硬件设备的直接接口,驱动程序屏蔽了硬件的细节。63嵌入式系统驱动程序概述设备驱动程序完成以下功能:对设备初始化和释放;对设备进行管理,包括实时参数设置,以及提供对设备的操作接口;读取应用程序传送给设备文件的数据或者回送应用程序请求的数据;检测和处理设备出现的错误。64Linux操作系统将所有的设备全部看成文件,并通过文件的操作界面进行操作。对用户程序而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说,是把设备映射为一个特殊的设备文件,用户程序可以像对其他文件一样对此设备文件进行操作。65这意味着:由于每一个设备至少由文件系统的一个文件代表,因而都有一个“文件名〞。应用程序通常可以通过系统调用open()翻开设备文件,建立起与目标设备的连接。翻开了代表着目标设备的文件,即建立起与设备的连接后,可以通过read()、write()、ioctl()等常规的文件操作对目标设备进行操作。66嵌入式系统驱动程序结构在系统内部,I/O设备的存取通过一组固定的入口点来进行,入口点也可以理解为设备的句柄,就是对设备进行操作的根本函数。字符型设备驱动程序提供如下几个入口点:open入口点。翻开设备准备I/O操作。对字符设备文件进行翻开操作,都会调用设备的open入口点。open子程序必须对将要进行的I/O操作做好必要的准备工作,如去除缓冲区等。如果设备是独占的,即同一时刻只能有一个程序访问此设备,那么open子程序必须设置一些标志以表示设备处于忙状态。67嵌入式系统驱动程序结构close入口点。关闭一个设备。当最后一次使用设备完成后,调用close子程序。独占设备必须标记设备方可再次使用。read入口点。从设备上读数据。对于有缓冲区的I/O操作,一般是从缓冲区里读数据。对字符设备文件进行读操作将调用read子程序。68嵌入式系统驱动程序结构write入口点。往设备上写数据。对于有缓冲区的I/O操作,一般是把数据写入缓冲区里。ioctl入口点。执行读、写之外的操作。select入口点。检查设备,看数据是否可读或设备是否可用于写数据。select系统调用在检查与设备文件相关的文件描述符时使用select入口点。69嵌入式系统驱动程序结构Linux系统设备驱动程序三个重要数据结构:file_operation〔文件操作〕file〔文件〕inode〔节点〕定义在include/linux/fs.h文件中

70嵌入式系统驱动程序结构file_operation将系统调用和驱动关联起来,用来存储内核驱动模块提供的对设备进行各种操作的指针函数。71structfile_operation{

structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);int(*readdir)(structfile*,void*,filldir_t);nsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,structvm_area_struct*);int(*open)(structinode*,structfile*);int(*flush)(structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*,intdatasync);int(*fasync)(int,structfile*,int);int(*lock)(structfile*,int,structfile_lock*);ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);};};72file_operation结构中的成员几乎全部是函数指针,所以实质上就是函数跳转表。每个进程对设备的操作都会根据major、minor设备号,转换成对file_operation结构的访问。73file_operation结构中常用的操作:lseek,移动文件指针的位置,只能用于可以随机存取的设备。read,进行读操作,如果该成员为一个空指针,系统调用返回一个-EINVAL错误,正常情况下返回一个非负整数代表读取的字节数。write,进行写操作。select,进行选择操作。如果驱动程序没有提供select入口,select操作会认为设备已经准备好进行任何I/O操作。ioctl,进行读、写以外的其他操作。open,翻开设备准备进行I/O操作。返回0表示翻开成功,返回负数表示失败。如果驱动程序没有提供open入口,那么只要/dev/driver文件存在就认为翻开成功。release,即close操作。74structfile主要用于与文件系统相关的设备驱动程序,可提供关于被翻开的文件的信息,定义如下:structfile{structlist_head f_list;structdentry *f_dentry;structvfsmount*f_vfsmnt;structfile_operations *f_op;atomic_t f_count;unsignedint f_flags;mode_t f_mode;loff_t f_pos;unsignedlong f_reada,f_ramax,f_raend,f_ralen,f_rawin;structfown_struct f_owner;unsignedint f_uid,f_gid;int f_error;unsignedlong f_version

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论