《Linux设备驱动程序中的代码分析--并发和竞争情况4208.docx》由会员分享,可在线阅读,更多相关《Linux设备驱动程序中的代码分析--并发和竞争情况4208.docx(85页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Linuux DDeviice Driiverr书籍(66)并发发和竞争争情况 (20008-09-28 16:28) 分类: LDDD3第 6 章 高高级字符符驱动操操作在第 33 章, 我们们建立了了一个完完整的设设备驱动动, 用用户可用用来写入入和读取取. 但但是一个个真正的的设备常常常提供供比同步步读和写写更多的的功能. 现在在我们已已装备有有调试工工具如果果发生错错误, 并且一一个牢固固的并发发的理解解来帮助助避免事事情进入入错误- 我我们可安安全地前前进并且且创建一一个更高高级的驱驱动.本章检查查几个你你需要理理解的概概念来编编写全特特性的字字符设备备驱动. 我们们从实现现 ioo
2、ctll 系统统调用开开始, 它是用用作设备备控制的的普通接接口. 接着我我们进入入各种和和用户空空间同步步的方法法; 在在本章结结尾, 你有一一个充分分的认识识对于如如何使进进程睡眠眠(并且且唤醒它它们), 实现现非阻塞塞的 II/O, 并且且通知用用户空间间当你的的设备可可用来读读或写. 我们们以查看看如何在在驱动中中实现几几个不同同的设备备存取策策略来结结束.这里讨论论的概念念通过 scuull 驱动的的几个修修改版本本来演示示. 再再一次, 所有有的都使使用内存存中的虚虚拟设备备来实现现, 因因此你可可自己试试验这些些代码而而不必使使用任何何特别的的硬件. 到此此为止, 你可可能在想想
3、亲手使使用真正正的硬件件, 但但是那将将必须等等到第 9 章章.6.1.iocctl 接口大部分驱驱动需要要 - 除了了读写设设备的能能力 - 通通过设备备驱动进进行各种种硬件控控制的能能力. 大部分分设备可可进行超超出简单单的数据据传输之之外的操操作; 用户空空间必须须常常能能够请求求, 例例如, 设备锁锁上它的的门, 弹出它它的介质质, 报报告错误误信息, 改变变波特率率, 或或者自我我销毁. 这些些操作常常常通过过 iooctll 方法法来支持持, 它它通过相相同名子子的系统统调用来来实现.在用户空空间, iocctl 系统调调用有下下面的原原型:int iocctl(intt fdd,
4、 uunsiigneed llongg cmmd, .); 这个原型型由于这这些点而而凸现于于 Unnix 系统调调用列表表, 这这些点常常常表示示函数有有数目不不定的参参数. 在实际际系统中中, 但但是, 一个系系统调用用不能真真正有变变数目的的参数. 系统统调用必必须有一一个很好好定义的的原型, 因为为用户程程序可存存取它们们只能通通过硬件件的门门. 因此, 原型型中的点点不表示示一个变变数目的的参数, 而是是一个单单个可选选的参数数, 传传统上标标识为 chaar *arggp. 这些点点在那里里只是为为了阻止止在编译译时的类类型检查查. 第第 3 个参数数的实际际特点依依赖所发发出的特
5、特定的控控制命令令( 第第 2 个参数数 ). 一些些命令不不用参数数, 一一些用一一个整数数值, 以及一一些使用用指向其其他数据据的指针针. 使使用一个个指针是是传递任任意数据据到 iiocttl 调调用的方方法; 设备接接着可与与用户空空间交换换任何数数量的数数据.iocttl 调调用的非非结构化化特性使使它在内内核开发发者中失失宠. 每个 iocctl 命令, 基本本上, 是一个个单独的的, 常常常无文文档的系系统调用用, 并并且没有有方法以以任何类类型的全全面的方方式核查查这些调调用. 也难于于使非结结构化的的 iooctll 参数数在所有有系统上上一致工工作; 例如, 考虑虑运行在在
6、 322-位模模式的一一个用户户进程的的 644-位 系统. 结果果, 有有很大的的压力来来实现混混杂的控控制操作作, 只只通过任任何其他他的方法法. 可可能的选选择包括括嵌入命命令到数数据流(本章稍稍后我们们将讨论论这个方方法)或或者使用用虚拟文文件系统统, 要要么是 syssfs 要么是是设备特特定的文文件系统统. (我们将将在 114 章章看看 syssfs). 但但是, 事实是是 iooctll 常常常是最容容易的和和最直接接的选择择,对于于真正的的设备操操作.iocttl 驱驱动方法法有和用用户空间间版本不不同的原原型:int (*iiocttl) (sttrucct iinodde
7、 *inoode, sttrucct ffilee *ffilpp, uunsiigneed iint cmdd, uunsiigneed llongg arrg);inodde 和和 fiilp 指针是是对应应应用程序序传递的的文件描描述符 fd 的值, 和传传递给 opeen 方方法的相相同参数数. ccmd 参数从从用户那那里不改改变地传传下来, 并且且可选的的参数 argg 参数数以一个个 unnsiggnedd loong 的形式式传递, 不管管它是否否由用户户给定为为一个整整数或一一个指针针. 如如果调用用程序不不传递第第 3 个参数数, 被被驱动操操作收到到的 aarg 值是无无
8、定义的的. 因因为类型型检查在在这个额额外参数数上被关关闭, 编译器器不能警警告你如如果一个个无效的的参数被被传递给给 iooctll, 并并且任何何关联的的错误将将难以查查找.如果你可可能想到到的, 大部分分 iooctll 实现现包括一一个大的的 swwitcch 语语句来根根据 ccmd 参数, 选择择正确的的做法. 不同同的命令令有不同同的数值值, 它它们常常常被给予予符号名名来简化化编码. 符号号名通过过一个预预处理定定义来安安排. 定制的的驱动常常常声明明这样的的符号在在它们的的头文件件中; scuull.h 为为 scculll 声明明它们. 用户户程序必必须, 当然, 包含含那
9、个头头文件来来存取这这些符号号.6.1.1.选择 iocctl 命令在为 iiocttl 编编写代码码之前, 你需需要选择择对应命命令的数数字. 许多程程序员的的第一个个本能的的反应是是选择一一组小数数从0或或1开始始, 并并且从此此开始向向上. 但是, 有充充分的理理由不这这样做. iooctll 命令令数字应应当在这这个系统统是唯一一的, 为了阻阻止向错错误的设设备发出出正确的的命令而而引起的的错误. 这样样的不匹匹配不会会不可能能发生, 并且且一个程程序可能能发现它它自己试试图改变变一个非非串口输输入系统统的波特特率, 例如一一个 FFIFOO 或者者一个音音频设备备. 如如果这样样的
10、iiocttl 号号是唯一一的, 这个应应用程序序得到一一个 EEINVVAL 错误而而不是继继续做不不应当做做的事情情.为帮助程程序员创创建唯一一的 iiocttl 命命令代码码, 这这些编码码已被划划分为几几个位段段. LLinuux 的的第一个个版本使使用 116-位位数: 高 88 位是是关联这这个设备备的魔魔数, 低 8 位位是一个个顺序号号, 在在设备内内唯一. 这样样做是因因为 LLinuus 是是无能能的(他自己己的话); 一一个更好好的位段段划分仅仅在后来来被设想想. 不不幸的是是, 许许多驱动动仍然使使用老传传统. 它们不不得不: 改变变命令编编码会破破坏大量量的二进进制程
11、序序,并且且这不是是内核开开发者愿愿意见到到的.根据 LLinuux 内内核惯例例来为你你的驱动动选择 iocctl 号, 你应当当首先检检查 iinclludee/assm/iiocttl.hh 和 Doccumeentaatioon/iiocttl-nnumbber.txtt. 这这个头文文件定义义你将使使用的位位段: typpe(魔魔数), 序号号, 传传输方向向, 和和参数大大小. iocctl-nummberr.txxt 文文件列举举了在内内核中使使用的魔魔数,20 因此此你将可可选择你你自己的的魔数并并且避免免交叠. 这个个文本文文件也列列举了为为什么应应当使用用惯例的的原因.定义
12、 iiocttl 命命令号的的正确方方法使用用 4 个位段段, 它它们有下下列的含含义. 这个列列表中介介绍的新新符号定定义在 .typee 魔数. 只是选选择一个个数(在在参考了了 iooctll-nuumbeer.ttxt之之后)并并且使用用它在整整个驱动动中. 这个成成员是 8 位位宽(_IOCC_TYYPEBBITSS). numbber 序(顺序序)号. 它是是 8 位(_IOCC_NRRBITTS)宽宽. direectiion 数据传送送的方向向,如果果这个特特殊的命命令涉及及数据传传送. 可能的的值是 _IOOC_NNONEE(没有有数据传传输), _IIOC_REAAD, _
13、IOOC_WWRITTE, 和 _IOCC_REEAD|_IOOC_WWRITTE (数据在在2个方方向被传传送). 数据据传送是是从应用用程序的的观点来来看待的的; _IOCC_REEAD 意思是是从设备备读, 因此设设备必须须写到用用户空间间. 注注意这个个成员是是一个位位掩码, 因此此 _IIOC_REAAD 和和 _IIOC_WRIITE 可使用用一个逻逻辑 AAND 操作来来抽取.sizee 涉及到的的用户数数据的大大小. 这个成成员的宽宽度是依依赖体系系的, 但是常常常是 13 或者 14 位. 你可为为你的特特定体系系在宏 _IOOC_SSIZEEBITTS 中中找到它它的值.
14、你使使用这个个 siize 成员不不是强制制的 - 内核核不检查查它 - 但但是它是是一个好好主意. 正确确使用这这个成员员可帮助助检测用用户空间间程序的的错误并并使你实实现向后后兼容, 如果果你曾需需要改变变相关数数据项的的大小. 如果果你需要要更大的的数据结结构, 但是, 你可可忽略这这个 ssizee 成员员. 我我们很快快见到如如何使用用这个成成员.头文件 , 它包含含在 中, 定义宏宏来帮助助建立命命令号, 如下下: _IO(typpe,nnr)(给没有有参数的的命令), _IORR(tyype, nrre, dattatyype)(给从从驱动中中读数据据的), _IIOW(typp
15、e,nnr,ddataatyppe)(给写数数据), 和 _IOOWR(typpe,nnr,ddataatyppe)(给双向向传送). ttypee 和 nummberr 成员员作为参参数被传传递, 并且 sizze 成成员通过过应用 sizzeoff 到 dattatyype 参数而而得到.这个头文文件还定定义宏, 可被被用在你你的驱动动中来解解码这个个号: _IOOC_DDIR(nr), _IOCC_TYYPE(nr), _IOCC_NRR(nrr), 和 _IOCC_SIIZE(nr). 我我们不进进入任何何这些宏宏的细节节, 因因为头文文件是清清楚的, 并且且在本节节稍后有有例子代代码
16、展示示.这里是一一些 iiocttl 命命令如何何在 ssculll 被被定义的的. 特特别地, 这些些命令设设置和获获得驱动动的可配配置参数数./* UUse k ass maagicc nuumbeer */#deffinee SCCULLL_IOOC_MMAGIIC k/* PPleaase usee a diffferrentt 8-bitt nuumbeer iin yyourr coode */#deffinee SCCULLL_IOOCREESETT _IIO(SSCULLL_IIOC_MAGGIC, 0)/* * SS meeanss SSet thhrouugh a pptr
17、, * TT meeanss TTelll ddireectlly wwithh thhe aarguumennt vvaluue * GG meeanss GGet: rreplly bby ssetttingg thhrouugh a ppoinnterr * QQ meeanss QQuerry: reespoonsee iss onn thhe rretuurn vallue * XX meeanss eeXchhangge: swwitcch GG annd SS attomiicallly * HH meeanss ssHifft: swwitcch TT annd QQ atto
18、miicallly */#deffinee SCCULLL_IOOCSQQUANNTUMM _IIOW(SCUULL_IOCC_MAAGICC, 11, iint)#deffinee SCCULLL_IOOCSQQSETT _IIOW(SCUULL_IOCC_MAAGICC, 22, iint)#deffinee SCCULLL_IOOCTQQUANNTUMM _IIO(SSCULLL_IIOC_MAGGIC, 3)#deffinee SCCULLL_IOOCTQQSETT _IIO(SSCULLL_IIOC_MAGGIC, 4)#deffinee SCCULLL_IOOCGQQUANNTUM
19、M _IIOR(SCUULL_IOCC_MAAGICC, 55, iint)#deffinee SCCULLL_IOOCGQQSETT _IIOR(SCUULL_IOCC_MAAGICC, 66, iint)#deffinee SCCULLL_IOOCQQQUANNTUMM _IIO(SSCULLL_IIOC_MAGGIC, 7)#deffinee SCCULLL_IOOCQQQSETT _IIO(SSCULLL_IIOC_MAGGIC, 8)#deffinee SCCULLL_IOOCXQQUANNTUMM _IIOWRR(SCCULLL_IOOC_MMAGIIC, 9, intt)#def
20、finee SCCULLL_IOOCXQQSETT _IIOWRR(SCCULLL_IOOC_MMAGIIC,110, intt)#deffinee SCCULLL_IOOCHQQUANNTUMM _IIO(SSCULLL_IIOC_MAGGIC, 111)#deffinee SCCULLL_IOOCHQQSETT _IIO(SSCULLL_IIOC_MAGGIC, 122)#deffinee SCCULLL_IOOC_MMAXNNR 114真正的源源文件定定义几个个额外的的这里没没有出现现的命令令.我们选择择实现 2 种种方法传传递整数数参数: 通过过指针和和通过明明确的值值(尽管管, 由由
21、于一个个已存在在的惯例例, iiocllt 应应当通过过指针交交换值). 类类似地, 2 种方法法被用来来返回一一个整数数值:通通过指针针和通过过设置返返回值. 这个个有效只只要返回回值是一一个正的的整数; 如同同你现在在所知道道的, 在从任任何系统统调用返返回时, 一个个正值被被保留(如同我我们在 reaad 和和 wrritee 中见见到的), 而而一个负负值被看看作一个个错误并并且被用用来在用用户空间间设置 errrno.21excchannge和sshifft操操作对于于 scculll 没有有特别的的用处. 我们们实现excchannge来显示示驱动如如何结合合独立的的操作到到单个的
22、的原子的的操作, 并且且shhiftt来连连接ttelll和queery. 有有时需要要象这样样的原子子的测试试-和-设置操操作, 特别地地, 当当应用程程序需要要设置和和释放锁锁.命令的明明确的序序号没有有特别的的含义. 它只只用来区区分命令令. 实实际上, 你甚甚至可使使用相同同的序号号给一个个读命令令和一个个写命令令, 因因为实际际的 iiocttl 号号在方方向位位是不同同的, 但是你你没有理理由这样样做. 我们选选择在任任何地方方不使用用命令的的序号除除了声明明中, 因此我我们不分分配一个个返回值值给它. 这就就是为什什么明确确的号出出现在之之前给定定的定义义中. 这个例例子展示示了
23、一个个使用命命令号的的方法, 但是是你有自自由不这这样做.除了少数数几个预预定义的的命令(马上就就讨论), iiocttl 的的 cmmd 参参数的值值当前不不被内核核使用, 并且且在将来来也很不不可能. 因此此, 你你可以, 如果果你觉得得懒, 避免前前面展示示的复杂杂的声明明并明确确声明一一组调整整数字. 另一一方面, 如果果你做了了, 你你不会从从使用这这些位段段中获益益, 并并且你会会遇到困困难如果果你曾提提交你的的代码来来包含在在主线内内核中. 头文文件 是是这个老老式方法法的例子子, 使使用 116-位位的调整整值来定定义 iiocttl 命命令. 那个源源代码依依靠调整整数因为为
24、使用那那个时候候遵循的的惯例, 不是是由于懒懒惰. 现在改改变它可可能导致致无理由由的不兼兼容.6.1.2.返回值值iocttl 的的实现常常常是一一个 sswittch 语句, 基于于命令号号. 但但是当命命令号没没有匹配配一个有有效的操操作时缺缺省的选选择应当当是什么么? 这这个问题题是有争争议的. 几个个内核函函数返回回 -EENIVVAL(Innvallid arggumeent), 它有意意义是因因为命令令参数确确实不是是一个有有效的. POOSIXX 标准准, 但但是, 说如果果一个不不合适的的 iooctll 命令令被发出出, 那那么 -ENOOTTYY 应当当被返回回. 这这个
25、错误误码被 C 库库解释为为设备备的不适适当的 iocctl, 这这常常正正是程序序员需要要听到的的. 然然而, 它仍然然是相当当普遍的的来返回回 -EEINVVAL, 对于于响应一一个无效效的 iiocttl 命命令.6.1.3.预定义义的命令令尽管 iiocttl 系系统调用用最常用用来作用用于设备备, 内内核能识识别几个个命令. 注意意这些命命令, 当用到到你的设设备时, 在你你自己的的文件操操作被调调用之前前被解码码. 因因此, 如果你你选择相相同的号号给一个个你的 iocctl命命令, 你不会会看到任任何的给给那个命命令的请请求, 并且应应用程序序获得某某些不期期望的东东西, 因为在
26、在 iooctll 号之之间的冲冲突.预定义命命令分为为 3 类: 可对任何何文件发发出的(常规, 设备备, FFIFOO, 或或者 ssockket) 的那那些. 只对常规规文件发发出的那那些. 对文件系系统类型型特殊的的那些.最后一类类的命令令由宿主主文件系系统的实实现来执执行(这这是 cchatttr 命令如如何工作作的). 设备备驱动编编写者只只对第一一类命令令感兴趣趣, 它它们的魔魔数是 T. 查查看其他他类的工工作留给给读者作作为练习习; eext22_iooctll 是最最有趣的的函数(并且比比预期的的要容易易理解), 因因为它实实现 aappeend-onlly 标标志和 im
27、mmutaablee 标志志.下列 iiocttl 命命令是预预定义给给任何文文件, 包括设设备特殊殊的文件件:FIOCCLEXX 设置 cclosse-oon-eexecc 标志志(Fiile IOcctl Cloose on EXeec). 设置置这个标标志使文文件描述述符被关关闭, 当调用用进程执执行一个个新程序序时.FIONNCLEEX 清除 cclosse-nno-eexecc 标志志(Fiile IOcctl Nott CLLosee onn EXXec). 这这个命令令恢复普普通文件件行为, 复原原上面 FIOOCLEEX 所所做的. FIIOASSYNCC 为这这个文件件设置或
28、或者复位位异步通通知(如如同在本本章中异步通通知一一节中讨讨论的). 注注意直到到 Liinuxx 2.2.44 版本本的内核核不正确确地使用用这个命命令来修修改 OO_SYYNC 标志. 因为为两个动动作都可可通过 fcnntl 来完成成, 没没有人真真正使用用 FIIOASSYNCC 命令令, 它它在这里里出现只只是为了了完整性性.FIOQQSIZZE 这个命令令返回一一个文件件或者目目录的大大小; 当用作作一个设设备文件件, 但但是, 它返回回一个 ENOOTTYY 错误误.FIONNBIOO Fille IIOcttl NNon-Bloockiing I/OO(在在阻塞塞和非阻阻塞操作
29、作一节节中描述述). 这个调调用修改改在 ffilpp-ff_fllagss 中的的 O_NONNBLOOCK 标志. 给这这个系统统调用的的第 33 个参参数用作作指示是是否这个个标志被被置位或或者清除除. (我们将将在本章章看到这这个标志志的角色色). 注意常常用的改改变这个个标志的的方法是是使用 fcnntl 系统调调用, 使用 F_SSETFFL 命命令.列表中的的最后一一项介绍绍了一个个新的系系统调用用, ffcnttl, 它看来来象 iiocttl. 事实上上, ffcnttl 调调用非常常类似 iocctl, 它也也是获得得一个命命令参数数和一个个额外的的(可选选地)参参数. 它
30、保持持和 iiocttl 独独立主要要是因为为历史原原因: 当 UUnixx 开发发者面对对控制 I/OO 操作作的问题题时, 他们决决定文件件和设备备是不同同的. 那时, 有 iocctl 实现的的唯一设设备是 ttyys, 它解释释了为什什么 -ENOOTTYY 是标标准的对对不正确确 iooctll 命令令的回答答. 事事情已经经改变, 但是是 fccntll 保留留为一个个独立的的系统调调用.6.1.4.使用 iocctl 参数在看 ssculll 驱驱动的 iocctl 代码之之前, 我们需需要涉及及的另一一点是如如何使用用这个额额外的参参数. 如果它它是一个个整数, 就容容易: 它
31、可以以直接使使用. 如果它它是一个个指针, 但是是, 必必须小心心些.当用一个个指针引引用用户户空间, 我们们必须确确保用户户地址是是有效的的. 试试图存取取一个没没验证过过的用户户提供的的指针可可能导致致不正确确的行为为, 一一个内核核 ooops, 系统统崩溃, 或者者安全问问题. 它是驱驱动的责责任来对对每个它它使用的的用户空空间地址址进行正正确的检检查, 并且返返回一个个错误如如果它是是无效的的.在第 33 章, 我们们看了 coppy_ffromm_usser 和 ccopyy_too_usser 函数, 它们们可用来来安全地地移动数数据到和和从用户户空间. 这些些函数也也可用在在
32、iooctll 方法法中, 但是 iocctl 调用常常常包含含小数据据项, 可通过过其他方方法更有有效地操操作. 开始, 地址址校验(不传送送数据)由函数数 acccesss_ook 实实现, 它定义义在 :int acccesss_okk(innt ttypee, cconsst vvoidd *aaddrr, uunsiigneed llongg siize); 第一个参参数应当当是 VVERIIFY_REAAD 或或者 VVERIIFY_WRIITE, 依据据这个要要进行的的动作是是否是读读用户空空间内存存区或者者写它. adddr 参数持持有一个个用户空空间地址址, ssizee 是
33、一一个字节节量. 例如, 如果果 iooctll 需要要从用户户空间读读一个整整数, sizze 是是 siizeoof(iint). 如如果你需需要读和和写给定定地址, 使用用 VEERIFFY_WWRITTE, 因为它它是 VVERIIRY_REAAD 的的超集.不象大部部分的内内核函数数, aacceess_ok 返回一一个布尔尔值: 1 是是成功(存取没没问题)和 00 是失失败(存存取有问问题). 如果果它返回回假, 驱动应应当返回回 -EEFAUULT 给调用用者.关于 aacceess_ok有有多个有有趣的东东西要注注意. 首先, 它不不做校验验内存存存取的完完整工作作; 它它只
34、检查查看这个个内存引引用是在在这个进进程有合合理权限限的内存存范围中中. 特特别地, acccesss_ook 确确保这个个地址不不指向内内核空间间内存. 第22, 大大部分驱驱动代码码不需要要真正调调用 aacceess_ok. 后面面描述的的内存存存取函数数为你负负责这个个. 但但是, 我们来来演示它它的使用用, 以以便你可可见到它它如何完完成.sculll 源源码利用用了 iiocllt 号号中的位位段来检检查参数数, 在在 swwitcch 之之前:int errr = 0, tmpp;int rettvall = 0;/* * eextrractt thhe ttypee annd
35、nnumbber bittfieeldss, aand donnt deccodee * wwronng ccmdss: rretuurn ENOOTTYY (iinappproopriiatee iooctll) bbefoore acccesss_okk() */if (_IOOC_TTYPEE(cmmd) != SCUULL_IOCC_MAAGICC) reeturrn -ENOOTTYY;if (_IOOC_NNR(ccmd) SCUULL_IOCC_MAAXNRR) reeturrn -ENOOTTYY;/* * tthe dirrecttionn iss a bittmassk,
36、andd VEERIFFY_WWRITTE ccatcchess R/W * ttrannsfeers. TTypee iis uuserr-orrienntedd, wwhille * aacceess_ok is kerrnell-orrienntedd, sso tthe connceppt oof reaad andd * wriite iss reeverrsedd */if (_IOOC_DDIR(cmdd) & _IIOC_REAAD) errr = !aacceess_ok(VERRIFYY_WRRITEE, (voiid _usser *)aarg, _IIOC_SIZZE(
37、ccmd);elsee iff (_IOCC_DIIR(ccmd) & _IOOC_WWRITTE) errr = !aacceess_ok(VERRIFYY_REEAD, (vvoidd _useer *)arrg, _IOOC_SSIZEE(cmmd);if (errr) reeturrn -EFAAULTT;在调用 acccesss_okk 之后后, 驱驱动可安安全地进进行真正正的传输输. 加加上 ccopyy_frrom_useer 和和 coopy_to_useer_ 函数, 程序序员可利利用一组组为被最最多使用用的数据据大小(1, 2, 4, 和 88 字节节)而优优化过的的函数.
38、 这些些函数在在下面列列表中描描述, 它们定定义在 :put_useer(ddatuum, ptrr) _puut_uuserr(daatumm, pptr) 这些宏定定义写 dattum 到用户户空间; 它们们相对快快, 并并且应当当被调用用来代替替 coopy_to_useer 无无论何时时要传送送单个值值时. 这些宏宏已被编编写来允允许传递递任何类类型的指指针到 putt_usser, 只要要它是一一个用户户空间地地址. 传送的的数据大大小依赖赖 prrt 参参数的类类型, 并且在在编译时时使用 sizzeoff 和 typpeoff 等编编译器内内建宏确确定. 结果是是, 如如果 pp
39、rt 是一个个 chhar 指针, 传送送一个字字节, 以及对对于 22, 44, 和和 可能能的 88 字节节.put_useer 检检查来确确保这个个进程能能够写入入给定的的内存地地址. 它在成成功时返返回 00, 并并且在错错误时返返回 -EFAAULTT. _puut_uuserr 进行行更少的的检查(它不调调用 aacceess_ok), 但但是仍然然能够失失败如果果被指向向的内存存对用户户是不可可写的. 因此此, _puut_uuserr 应当当只用在在内存区区已经用用 acccesss_ook 检检查过的的时候.作为一个个通用的的规则, 当你你实现一一个 rreadd 方法法时,
40、 调用 _pput_useer 来来节省几几个周期期, 或或者当你你拷贝几几个项时时, 因因此, 在第一一次数据据传送之之前调用用 acccesss_ook 一一次, 如同上上面 iiocttl 所所示.get_useer(llocaal, ptrr) _geet_uuserr(loocall, pptr) 这些宏定定义用来来从用户户空间接接收单个个数据. 它们们象 pput_useer 和和 _putt_usser, 但是是在相反反方向传传递数据据. 获获取的值值存储于于本地变变量 llocaal; 返回值值指出这这个操作作是否成成功. 再次, _gett_usser 应当只只用在已已经使用
41、用 acccesss_ook 校校验过的的地址.如果做一一个尝试试来使用用一个列列出的函函数来传传送一个个不适合合特定大大小的值值, 结结果常常常是一个个来自编编译器的的奇怪消消息, 例如covverssionn too noon-sscallar typpe rrequuestted. 在在这些情情况中, 必须须使用 coppy_tto_uuserr 或者者 coopy_froom_uuserr.6.1.5.兼容性性和受限限操作存取一个个设备由由设备文文件上的的许可权权控制, 并且且驱动正正常地不不涉及到到许可权权的检查查. 但但是, 有些情情形, 在保证证给任何何用户对对设备的的读写许许可
42、的地地方, 一些控控制操作作仍然应应当被拒拒绝. 例如, 不是是所有的的磁带驱驱动器的的用户都都应当能能够设置置它的缺缺省块大大小, 并且一一个已经经被给予予对一个个磁盘设设备读写写权限的的用户应应当仍然然可能被被拒绝来来格式化化它. 在这样样的情况况下, 驱动必必须进行行额外的的检查来来确保用用户能够够进行被被请求的的操作.传统上 uniix 系系统对超超级用户户帐户限限制了特特权操作作. 这这意味着着特权是是一个全全有-或或-全无无的东西西 - 超级级用户可可能任意意做任何何事情, 但是是所有其其他的用用户被高高度限制制了. Linnux 内核提提供了一一个更加加灵活的的系统, 称为为能力
43、. 一个个基于能能力的系系统丢弃弃了全有有-或全全无模式式, 并并且打破破特权操操作为独独立的子子类. 这种方方式, 一个特特殊的用用户(或或者是程程序)可可被授权权来进行行一个特特定的特特权操作作而不必必泄漏进进行其他他的, 无关的的操作的的能力. 内核核在许可可权管理理上排他他地使用用能力, 并且且输出 2 个个系统调调用 ccapgget 和 ccapsset, 来允允许它们们被从用用户空间间管理.全部能力力可在 中找到到. 这这些是对对系统唯唯一可用用的能力力; 对对于驱动动作者或或者系统统管理员员, 不不可能不不修改内内核源码码而来定定义新的的. 设设备驱动动编写者者可能感感兴趣的的
44、这些能能力的一一个子集集, 包包括下面面:CAP_DACC_OVVERRRIDEE 这个能力力来推翻翻在文件件和目录录上的存存取的限限制(数数据存取取控制, 或者者 DAAC).CAP_NETT_ADDMINN 进行网络络管理任任务的能能力, 包括那那些能够够影响网网络接口口的.CAP_SYSS_MOODULLE 加载或去去除内核核模块的的能力.CAP_SYSS_RAAWIOO 进行 raww II/O 操作的的能力. 例子子包括存存取设备备端口或或者直接接和 UUSB 设备通通讯.CAP_SYSS_ADDMINN 一个捕获获-全部部的能力力, 提提供对许许多系统统管理操操作的存存取.CAP_
45、SYSS_TTTY_CCONFFIG 进行 ttty 配置任任务的能能力.在进行一一个特权权操作之之前, 一个设设备驱动动应当检检查调用用进程有有合适的的能力; 不这这样做可可能导致致用户进进程进行行非法的的操作, 对系系统的稳稳定和安安全有坏坏的后果果. 能能力检查查是通过过 caapabble 函数来来进行的的(定义义在 ): intt caapabble(intt caapabbiliity); 在 scculll 例子子驱动中中, 任任何用户户被许可可来查询询 quuanttum 和 qquanntumm 集的的大小. 只有有特权用用户, 但是, 可改改变这些些值, 因为不不适当的的值可能能很坏地地影响系系统性能能. 当当需要时时, iiocttl 的的 scculll 实现现检查用用户的特特权级别别, 如如下: if (! cappablle (CAPP_SYYS_AADMIIN) retturnn -EEPERRM;在这个任任务缺乏乏一个更更加特定定的能力力时, CAPP_SYYS_AADMIIN 被被选择来来做这个个测试.6.1.6.iocctl 命令的的实现iocttl 的的 scculll 实现现只传递递设备的的