⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 (ldd) ch17-最新进展(转载).htm

📁 LINUX驱动编程
💻 HTM
📖 第 1 页 / 共 4 页
字号:
<TABLE borderColor=#666666 cellPadding=2 width="90%" align=center border=2>
  <TBODY>
  <TR>
    <TD bgColor=#000000>
      <P align=center><A href="http://joyfire.net/lsdp/index.htm"><FONT 
      color=#ffffff size=2>目录页</FONT></A> | <A 
      href="http://joyfire.net/lsdp/20.htm"><FONT color=#ffffff 
      size=2>上一页</FONT></A></P>
      <P align=center><FONT face=黑体 color=#ffffff size=6>(LDD) Ch17-最新进展(转载) 
      </FONT></P><SPAN style="LINE-HEIGHT: 1; LETTER-SPACING: 0pt"><FONT 
      color=#ffffff size=3>
      <P>发信人:&nbsp;Altmayer&nbsp;(alt),&nbsp;信区:&nbsp;GNULinux<BR>标&nbsp;&nbsp;题:&nbsp;(LDD)&nbsp;Ch17-最新进展(转载)<BR>发信站:&nbsp;饮水思源&nbsp;(2001年12月13日08:58:05&nbsp;星期四),&nbsp;站内信件<BR>&nbsp;<BR>【&nbsp;以下文字转载自&nbsp;<FONT 
      color=#00ff00>UNIXpost&nbsp;</FONT>讨论区&nbsp;】<BR>【&nbsp;原文由<FONT 
      color=#00ff00>&nbsp;altmayer.bbs@bbs.nju.edu.cn,</FONT>&nbsp;所发表&nbsp;】<BR>&nbsp;<BR>【&nbsp;以下文字转载自&nbsp;<FONT 
      color=#00ff00>altmayer&nbsp;</FONT>的信箱&nbsp;】<BR>&nbsp;<BR>第十七章&nbsp;最新进展<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>Linux一直在迅速地发展着,开发人员总是迫切希望改善核心内部,它们并不考虑向后兼<BR>容性。这种自由开发导致了不同版本核心提供的设备驱动程序接口之间一定程度的不兼<BR>容。不过,在应用级还保持着兼容,除了个别需要与核心特征进行低级交互的应用(象p<BR>s)。<BR>&nbsp;<BR>另一方面,设备驱动程序是直接链接到核心映象上的,因此必须与数据结构、全局变量<BR>、以及由内核系统引出的函数发生的改变保持一致。在开发过程中,随着新特征的加入<BR>,内部被修改;新的实现取代了就的实现,因为实践证明它们更快,更清晰。尽管不兼<BR>容性要求程序员在写模块时要做一些额外的工作,我认为连续的开发是Linux社区的成功<BR>点:严格的先后兼容性最终证明是有害的。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>点:严格的先后兼容性最终证明是有害的。<BR>&nbsp;<BR>这一章讲述2.0.x和2.1.43之间的不同,这些将会与即将推出的2.2发布类似。Linus在前<BR>几个2.1版本中引入了最重要的改变,这样核心就可以多经历几个2.1版本,使得驱动程<BR>序的作者有足够的时间在开发被锁定以发布稳定的2.2之前来稳定驱动程序。下面的小节<BR>介绍驱动程序是如何处理2.0和2.1.43之间的不同的。我已经修改了本书介绍的所有示例<BR>代码,使得它们可以同时在2.0和2.1.43上编译和运行,以及这之间的大多数版本。<BR>&nbsp;<BR>驱动程序的新版本可以从O’Reilly的FTP站点上在线例子的v2.1目录下得到。2.0和2.1<BR>之间的兼容性通过头文件sysdep-2.1.h获得,它可以与你自己的模块集成。我选择不把<BR>兼容性扩展到1.2避免了给C代码加载太多的条件,而且1.2-2.0的不同已经在前面的章节<BR>解释过了。在我将要写完这本书时,我了解到从2.1.43起又引入了一些小的不兼容性;<BR>我不打算对之加以评述,因为我不能保证对这些最新版本的完全支持。<BR>&nbsp;<BR>注意在本章我不会讲述2.1开发系列引入的所有新东西。我要做的只是移植2.0模块,使<BR>之可以在2.0和2.1核心上运行。利用2.1的特征意味着放弃对不具有这些特征的2.0发布<BR>的支持。2.0版本仍是本书的重点。<BR>&nbsp;<BR>在写sysdep-2.1.h时,我已努力使你熟悉新的API,我引入的宏用来使2.1的代码可以在2<BR>..0上跑,而不是相反。<BR>&nbsp;<BR>本章以重要性逐渐降低的顺序介绍不兼容性;最重要的不同首先被介绍,次要的细节则<BR>在后面介绍。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>在后面介绍。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>模块化<BR>&nbsp;<BR>在Linux社区中,模块化变的越来越重要,开发人员决定用一个更清晰的实现取代旧的。<BR>头文件&lt;linux/module.h&gt;在2.1.18中完全重写了,一个新的API被引入。如你所期望的,<BR>新的实现比旧的要容易使用。<BR>&nbsp;<BR>为了加载你的模块,你将需要包modutils-2.1.34甚至更新版本(细节见Documentation/<BR>Changes)。当与旧的核心一起使用时,这个包可以回到兼容模式,因此你可以用这个新<BR>包替换modules-2.0.0,即使你经常在2.0和2.1之间切换。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>引出符号<BR>&nbsp;<BR>符号表的新接口比以前的要容易多了,它依赖于下面的宏:<BR>&nbsp;<BR>EXPORT_NO_SYMBOLS;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;这个宏与register_symtab(NULL)等价;它可以出现在一个函数的内部或外部,因为<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;这个宏与register_symtab(NULL)等价;它可以出现在一个函数的内部或外部,因为<BR>它只是指导汇编器,而不产生实际代码。如果你想在Linux2.0上编译模块,这个宏应该<BR>在init_module中被使用。<BR>&nbsp;<BR>EXPORT_SYMTAB;<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;如果你打算引出一些符号,那么模块必须在包含&lt;linux/module.h&gt;之前定义这个宏<BR>。<BR>&nbsp;<BR>EXPORT_SYMBOL(name);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;这个宏表明你想引出这个符号名。它必须在任何函数之外使用。<BR>&nbsp;<BR>EXPORT_SYMBOL_NOVERS(name)<BR>&nbsp;<BR>使用这个宏而不是EXPORT_SYMBOL()强制丢弃版本信息,即使是编译带有版本支持的代码<BR>。这对避免一些不必要的重编译很有用。例如,memset函数将总以同样的方式工作;引<BR>出符号而不带版本信息允许开发者改变实现(甚至使用的数据类型)而不需insmod标出<BR>不兼容性。在模块化的代码中不大可能需要这个宏。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>如果这些宏都没有在你的源码中使用,那么所有的非静态符号都被引出;这与在2.0中一<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>如果这些宏都没有在你的源码中使用,那么所有的非静态符号都被引出;这与在2.0中一<BR>样。如果这个模块是从几个源文件生成的,你可以从任何源文件引出符号,而且还可以<BR>在模块的范围中共享任何符号。<BR>&nbsp;<BR>如你所看到的,引出符号表的新方法解决了一些问题,但这个创新也引入了一个重要的<BR>不兼容性:一个引出了一些符号的模块,如果想同时在2.0和2.1上编译运行,则必须用<BR>条件编译来包含两个实现。下面是export模块(v2.1/misc-modules/export.c)如何处<BR>理这个问题的:<BR>&nbsp;<BR>(代码384&nbsp;#1)<BR>&nbsp;<BR>上面的代码依赖于下面sysdep-2.1.h中的行:<BR>&nbsp;<BR>(代码384&nbsp;#2)<BR>&nbsp;<BR>当使用2.1.18或更新的核心时,REGISTER_SYMTAB扩展为什么都不做,因为init_module<BR>中没有什么需要做的;在函数外使用EXPORT_SYMBOL是引出模块符号唯一需要做的。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>声明参数<BR>&nbsp;<BR>核心模块的新的实现利用了ELF二进制格式的特征以获得更好的灵活性。更特别地,当构<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>核心模块的新的实现利用了ELF二进制格式的特征以获得更好的灵活性。更特别地,当构<BR>造一个ELF目标文件时,你可以声明除“正文”、“数据”和“bss”之外的节。一个“<BR>节”是一个连续的数据区域,与“段”的概念类似。<BR>&nbsp;<BR>对于2.1,核心模块必须使用ELF二进制格式编译。事实上,2.1核心利用了ELF的节(见<BR>“处理核心空间错误”),只能编译为ELF。因此模块的限制并不是个真正限制。使用EL<BR>F允许信息域被存在目标文件中。好奇的读者可以使用objdump&nbsp;–section-headers来观<BR>察节头,用objdump&nbsp;–section=.modinfo&nbsp;–full-contents来查看模块特定的信息。实<BR>际上,.modinfo一节是用来存储模块信息的节,包含被称做“参数”的值,可以在加载<BR>时修改。<BR>&nbsp;<BR>当在2.1上编译时,一个参数可以用宏如下声明:<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MODULE_PARM(variable,&nbsp;type-description);<BR>&nbsp;<BR>当你在源文件中使用这个宏时,编译器被告知在目标文件中插入一个描述串;这个描述<BR>表明variable是个参数,它的类型对应于type-description。insmod和modprobe查看目<BR>标文件,保证你被允许修改variable,同时检查参数的实际类型。类型检查对防止不愉<BR>快的错误非常重要,例如用一个串覆盖了一个整数,或错把长整数当成了短整数。<BR>&nbsp;<BR>按我的观点,讲述宏的最好办法时给出几行示例代码。下面的代码属于一个想象的网卡<BR>:<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>(代码385)<BR>&nbsp;<BR>type-description串在头文件&lt;linux/module.h&gt;中被非常详细地介绍,并且为了你的方<BR>便,它可以在整个核心源码中找到。<BR>&nbsp;<BR>值得给出的一个技巧是如何参数化一个数组的长度,象上面的io。例如,设想网络驱动<BR>程序支持的外围板子的数目有宏MAX_DEVICES表示,而不是硬写入的数字4。出于这个目<BR>的,头文件&lt;linux/module.h&gt;定义了一个宏(__MODULE_STRING),它用C预处理器将一<BR>个宏“字符串化”。这个宏可以如下使用:<BR>&nbsp;<BR>int&nbsp;io[MAX_DEVICES+1]={0,};<BR>&nbsp;<BR>MODULE_PARM(io,&nbsp;“1-”&nbsp;__MODULE_STRING(MAX_DEVICES)&nbsp;“i”);<BR>&nbsp;<BR>在前一行中,被“字符串化”的值与其他串接在一起构成目标文件中有意义的串。<BR>&nbsp;<BR>scull示例模块也用MODULE_PARM来声明它的参数(scull_major和其他整数变量)。这在<BR>Linux2.0上编译时可能会出问题,那里这个宏未定义。我选择的简单的修正是在sysdep-<BR>2.1.h中定义MODULE_PARM,这样在与2.0头文件编译时,它扩展为空语句。<BR>&nbsp;<BR>其它有意义的值可以象MODULE_AUTHOR()一样&nbsp;存在模块的.modinfo一节,但它们目前没<BR>有使用。请参考&lt;linux/module.h&gt;以获得更多的信息。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>有使用。请参考&lt;linux/module.h&gt;以获得更多的信息。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>/proc/modules<BR>&nbsp;<BR>/proc/modules的格式在2.1.18中略有改变,而所有的模块化代码都被重写了。尽管这个<BR>改变并不影响源码,你可能对其细节不感兴趣,因为/proc/modules在模块开发时经常被<BR>检查。<BR>&nbsp;<BR>新格式和旧的一样是面向行的,每行包含下面的域:<BR>&nbsp;<BR>模块名<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个域与Linux2.0相同。<BR>&nbsp;<BR>模块大小<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这是个十进制数,以字节为单位(而不是内存页)报告长度。<BR>&nbsp;<BR>这个模块的使用计数<BR>&nbsp;<BR>如果模块没有使用计数,这个计数报告-1。这是和新的模块化代码一道引入的新特征;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>如果模块没有使用计数,这个计数报告-1。这是和新的模块化代码一道引入的新特征;<BR>你可以写一个模块,它的去除可以有一个函数控制而不是使用计数。这个函数判断模块<BR>是否能够被卸载。例如,ipv6模块就使用这个特征。<BR>&nbsp;<BR>可选标志<BR>&nbsp;<BR>标志是文本串,每个都由括号包含,并由空格分隔。<BR>&nbsp;<BR>参考本模块的模块列表<BR>&nbsp;<BR>这个列表整体被包含在方括号内,表中的单个名字由空格隔开。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>下面是/proc/modules在2.1.43中的可能内容:<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;morgana%&nbsp;cat&nbsp;/proc/modules<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ipv6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;57164&nbsp;&nbsp;&nbsp;-1<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;netlink&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3180&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;[ipv6]<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;floppy&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;45960&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;(autoclean)<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;floppy&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;45960&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;(autoclean)<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;monitor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;516&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;(unused)<BR>&nbsp;<BR>在这个屏幕快照中,ipv6没有使用计数,并依赖于netlink;floppy已经被kerneld加载<BR>,由“autoclean”标志给出,monitor是我的一个小工具,控制一些状态灯,并在系统<BR>终止时关掉我的计算机。如你所看到的,它是“unused”,我并不关心它的使用计数。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>文件操作<BR>&nbsp;<BR>有几个文件操作在2.1里与2.0有不同的原型。这主要是出于处理大小不能放入32位的文<BR>件的需要。其不同由头文件sysdep-2.1.h处理,它根据使用的核心版本定义了几个伪类<BR>型。文件操作中引入的仅有的显著创新是poll方法,它用完全不同的实现代替了select<BR>方法。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>原型的不同<BR>&nbsp;<BR>四个文件操作表征一个新的原型;它们是:<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>long&nbsp;long&nbsp;(*llseek)&nbsp;(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;long&nbsp;long,&nbsp;int);<BR>&nbsp;<BR>long&nbsp;(*read)&nbsp;(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;fle&nbsp;*,&nbsp;char&nbsp;*,&nbsp;unsigned&nbsp;long);<BR>&nbsp;<BR>long&nbsp;(*write)&nbsp;(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;const&nbsp;char&nbsp;*,&nbsp;unsigned&nbsp;long);<BR>&nbsp;<BR>int&nbsp;(*release)&nbsp;(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*);<BR>&nbsp;<BR>它们在2.0中的对应者是:<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;(*lseek)&nbsp;(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;off_t,&nbsp;int);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;(*read)&nbsp;(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;char&nbsp;*,&nbsp;int);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;(*write)&nbsp;(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;const&nbsp;char&nbsp;*,&nbsp;int);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;(*release)&nbsp;(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*);<BR>&nbsp;<BR>如你所见的,其不同在于它们的返回值(它允许了更大的范围),还有count和offset参<BR>数。头文件sysdep-2.1.h通过定义下面的宏处理这些不同:<BR>&nbsp;<BR>read_write_t<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>read_write_t<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个宏扩展为参数count的类型以及read和write的返回值。<BR>&nbsp;<BR>lssek_t<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个宏扩展为llseek的返回值类型。方法名字的改变(从lseek到llseek)并不<BR>是个问题,因为你一般在file_operations中并不用名字对域赋值,而是声明一个静态结<BR>构。<BR>&nbsp;<BR>lseek_off_t<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lseek的offset参数。<BR>&nbsp;<BR>release_t<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;release方法的返回值;或为void或为int;<BR>&nbsp;<BR>release_return(&nbsp;int&nbsp;return_value)<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个宏可以用来从release方法返回。它的参数用来返回一个错误代码:0表示成<BR>功,负值表示失败。在比2.1.31老的核心中,这个宏扩展为return,因为这个方法返回v<BR>oid。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>oid。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>用前面的宏,一个可移植的驱动程序原型是:<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lseek_t&nbsp;my_lseek(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;lseek_off_t,&nbsp;int);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;read_write_t&nbsp;my_read(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;char&nbsp;*,&nbsp;count_t);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;read_write_t&nbsp;my_write(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;const&nbsp;char&nbsp;*,<BR>count_t);<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;release_t&nbsp;my_release(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*);<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>poll方法<BR>&nbsp;<BR>2.1.23引入了poll系统调用,它是system&nbsp;V中select的对应者(由BSD&nbsp;Unix引入)。不<BR>幸的是不可能在select设备方法之上实现poll的功能,所以整个实现用不同的一个代替<BR>,它作为select和poll的后端。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>在当前版本的核心中,file_operations中的设备方法也叫poll,与系统调用类似,因为<BR>其内部模仿这个系统调用。这个方法的原型是:<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;int&nbsp;(*poll)&nbsp;(struct&nbsp;file&nbsp;*,&nbsp;poll_table&nbsp;*);<BR>&nbsp;<BR>驱动程序中设备特定的实现主要完成两个任务:<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在一个可能在将来唤醒它的等待队列中将当前进程排队。通常,这意味着同<BR>时在输入和输出队列中对进程排队。函数poll_wait被用于这个目的,其工作方式与sele<BR>ct_wait非常类似(细节请看第五章“增强的字符设备驱动程序操作”中“select”一节<BR>)。<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;构造一个位掩码描述设备的状态,并将其返回给调用者。这些位的值是平台<BR>特定的,在&lt;linux/poll.h&gt;中定义,它必须被包含在驱动程序中。<BR>&nbsp;<BR>在讲述位掩码的每一位前,我想给出一个典型的实现。下面的函数是v2.1/scull/pipe.c<BR>的一部分,是/dev/scullpipe的poll方法的实现。scullpipe的内部在第五章介绍过。<BR>&nbsp;<BR>(代码389)<BR>&nbsp;<BR>如你所看到的,这个代码相当简单。它比对应的select方法要容易。至于select,状态<BR>位计为“可读”、“可写”,或“发生例外”(这是select的第三个条件)。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>位计为“可读”、“可写”,或“发生例外”(这是select的第三个条件)。<BR>&nbsp;<BR>poll各位的完全列表在下面给出。“输入”位列在前面,然后是“输出”,一个“例外<BR>”位列在最后。<BR>&nbsp;<BR>POLLIN<BR>&nbsp;<BR>如果设备可以被无阻塞地读,那么这个位必须被设置。<BR>&nbsp;<BR>POLLRDNORM<BR>&nbsp;<BR>如果“一般”数据可以被读,这个位必须被设。一个可读设备返回(POLLIN&nbsp;|<BR>POLLRDNORM)。<BR>&nbsp;<BR>POLLRDBAND<BR>&nbsp;<BR>在目前的核心源码中这个位不被使用。Unix&nbsp;System&nbsp;V使用这个位报告非0优先级的数据<BR>可读。数据优先级的概念与“Streams”包相关。<BR>&nbsp;<BR>POLLHUP<BR>&nbsp;<BR>当一个读设备的进程看到文件结尾时,驱动程序必须设置POLLHUP(挂起)。一个调用sele<BR>ct的进程将被告知设备可读,这由select的功能说明。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>ct的进程将被告知设备可读,这由select的功能说明。<BR>&nbsp;<BR>POLLERR<BR>&nbsp;<BR>设备上发生了一个错误条件。当poll被select系统调用调用时,设备被报告为既可读又<BR>可写,因为read或write将无阻塞地返回一个错误代码。<BR>&nbsp;<BR>POLLOUT<BR>&nbsp;<BR>如果设备可以被无阻塞地写,这个位在返回值中被设置。<BR>&nbsp;<BR>POLLWRNORM<BR>&nbsp;<BR>这个位与POLLOUT有相同的含义,有时甚至的确为同一个数。一个可写的设备返回(POLL<BR>OUT&nbsp;|&nbsp;POLLWRNORM)。<BR>&nbsp;<BR>POLLWRBAND<BR>&nbsp;<BR>与POLLRDBAND类似,这个位意味着非0优先级的数据可以被写到设备。只有poll的“数据<BR>报”实现用到着位,因为一个数据报可以传送“无团队数据(out-of-band&nbsp;data)”。s<BR>elect报告设备是可写的。<BR>&nbsp;<BR>POLLPRI<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>POLLPRI<BR>&nbsp;<BR>高优先级的数据(“无团队的”)可以被无阻塞地读取。这个位导致select报告文件上<BR>发生了一个例外条件,因为select将无团队包作为一个例外条件报告。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>poll的主要问题是它与2.0核心所使用的select方法没有任何关系。因此,处理这个不同<BR>的最好方法是使用条件编译来编译合适的函数,而且同时将它们都包含在源文件中。<BR>&nbsp;<BR>如果当前版本支持select而不是poll,那么头文件sysdep-2.1.h定义符号__USE_OLD_SEL<BR>ECT__。这将你从在源码中必须引用LINUX_VERSION_CODE中解脱出来。v2.1目录下的示例<BR>驱动程序使用了与下面类似的代码:<BR>&nbsp;<BR>(代码390)<BR>&nbsp;<BR>(代码391)<BR>&nbsp;<BR>这两个函数用同样的名字调用,因为在结构sample_fops中sample_poll被引用,那里pol<BR>l文件操作代替了select方法。<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>访问用户空间<BR>&nbsp;<BR>核心的第一个2.1版引入了一种从核心代码访问用户空间的新(更好)方法。这个改变修<BR>正了一个长期存在的错误行为并增强了系统的性能。<BR>&nbsp;<BR>当你位核心2.1编译代码,并需要访问用户空间时,你需要包含&lt;asm/uaccess.h&gt;,而不<BR>是&lt;asm/segment.h&gt;。你还必须使用一个与2.0不同的函数集。不用说,头文件sysdep-2.<BR>1.h尽可能地照顾了这些不同,允许你在2.0上编译时使用2.1的语义。<BR>&nbsp;<BR>在用户访问中最令人注意的不同时verify_area没有了,因为多数验证都由CPU完成了。<BR>关于这个主题的细节见本章后面的“处理核心空间错误”。<BR>&nbsp;<BR>可被用来访问用户空间的新的函数集是:<BR>&nbsp;<BR>int&nbsp;access_ok(int&nbsp;type,&nbsp;unsigned&nbsp;long&nbsp;addr,&nbsp;unsigned&nbsp;long&nbsp;size);<BR>&nbsp;<BR>如果当前进程被允许访问地址addr处的内存,函数返回真(1),否则为假(0)。这个<BR>函数取代verify_area,尽管它进行较少的检查。和老的verify_area接收一样的参数,<BR>但是要快的多。在你复引用一个用户空间地址之前,这个函数应该被调用对之进行检查<BR>;如果你没有检查,用户有可能会访问和修改核心内存。本章后面的“虚拟内存”一节<BR>更细致地解释了这个问题。幸运的是,下面描述的大多数函数都替你进行了这个检查,<BR>因此你实际上并不需要调用access_ok,除非你选择这样做。<BR></P></FONT><FONT 
      color=#ffffff size=3>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -