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

📄 (ldd) ch03-字符设备驱动程序(转载).htm

📁 《linux设备驱动程序开发》
💻 HTM
📖 第 1 页 / 共 4 页
字号:
          <TD width="8%" height=4>
            <DIV align=center><A href="mailto:joyfire@sina.com"><FONT 
            color=#ffffff>联系</FONT></A></DIV></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<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/4.htm"><FONT color=#ffffff 
      size=2>上一页</FONT></A> | <A href="http://joyfire.net/lsdp/6.htm"><FONT 
      color=#ffffff size=2>下一页</FONT></A></P>
      <P align=center><FONT face=黑体 color=#ffffff size=6>(LDD) Ch03-字符设备驱动程序(转载) 
      </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;Ch03-字符设备驱动程序(转载)<BR>发信站:&nbsp;饮水思源&nbsp;(2001年12月13日08:57:11&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>第3章&nbsp;字符设备驱动程序<BR>&nbsp;<BR>&nbsp;<BR>本章的目标是编写一个完整的字符设备驱动程序。由于这类驱动程序适合于大多数简单<BR>的硬件设备,我们首先开放一个字符设备驱动程序。字符也相对比较好理解,比如说块<BR>设备驱动程序。我们的最终目标是写一个模块化的字符设备驱动程序,但本章我们不再<BR>讲述有关模块化的问题。<BR>&nbsp;<BR>本章通篇都是从一个真实的设备驱动程序截取出的代码块:这个设备就是scull,是“Si<BR>mple&nbsp;Character&nbsp;Utility&nbsp;for&nbsp;Loading&nbsp;Localities”的缩写。尽管scull是一个设备,<BR>但它却是操作内存的字符设备。这种情况的一个副作用就是,只要涉及scull,“设备”<BR>这个词就可以同“scull使用的内存区”互换使用。<BR>&nbsp;<BR>scull的优点是,由于每台电脑都有内存,所以它与硬件无关。scull用kmalloc分配内存<BR>,而且仅仅操作内存。任何人都可以编译和运行scull,而且scull可以移植到所有Linux<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>,而且仅仅操作内存。任何人都可以编译和运行scull,而且scull可以移植到所有Linux<BR>支持的平台上。但另一方面,除了演示内核于字符设备驱动程序间的交互过程,可以让<BR>用户运行某些测试例程外,scull做不了“有用的”事。<BR>&nbsp;<BR>scull的设计<BR>编写设备驱动程序的第一步就是定义驱动程序提供给用户程序的能力(“机制”)。由<BR>于我们的“设备”是电脑内存的一部分,我做什么都可以。它可以是顺便存取设备,也<BR>可以是随机存取设备,可以是一个设备,也可以是多个,等等。<BR>&nbsp;<BR>为了是scull更有用,可以成为编写真实设备的驱动程序的模板,我将向你展示如何在电<BR>脑的内存之上实现若干设备抽象操作,每一种操作都有自己的特点。<BR>&nbsp;<BR>scull的源码实现如下设备。由模块实现的每一种设备都涉及一种类型:<BR>&nbsp;<BR>scull0-3<BR>&nbsp;<BR>4个设备,共保护了4片内存区,都是全局性的和持久性的。“全局性”是指,如果打开<BR>设备多次,所有打开它的文件描述符共享其中的数据。“持久性”是指,如果设备关闭<BR>后再次打开,数据不丢失。由于可以使用常用命令访问这个设备,如cp,cat以及shell<BR>I/O重定向等,这个设备操作非常有趣;本章将深入探讨它的内部结构。<BR>&nbsp;<BR>scullpipe0-3<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>4个“fifo”设备,操作起来有点象管道。一个进程读取另一个进程写入的数据。如果有<BR>多个进程读同一个设备,他们彼此间竞争数据。通过scullpipe的内部结构可以了解阻塞<BR>型和非阻塞型读/写是如何实现的;没有中断也会出现这样的情况。尽管真实的驱动程序<BR>利用中断与它们的设备同步,但阻塞型和非阻塞型操作是非常重要的内容,从概念上讲<BR>与中断处理(第9章,中断处理,介绍)无关。<BR>&nbsp;<BR>scullsingle<BR>&nbsp;<BR>scullpriv<BR>&nbsp;<BR>sculluid<BR>&nbsp;<BR>scullwuid<BR>&nbsp;<BR>这些设备与scull0相似,但在何时允许open操作时都不同方式的限制。第一个(scullsi<BR>ngle)只允许一次一个进程使用驱动程序,而scullpriv对每个虚拟控制台是私有的(每<BR>个设备对虚拟控制台是私有的)。sculluid和scullwuid可以多次打开,但每次只能有一<BR>个用户;如果另一个用户锁住了设备,前者返回-EBUSY,而后者则实现为阻塞型open。<BR>通过这些可以展示如何实现不同的访问策略。<BR>&nbsp;<BR>每一个scull设备都展示了驱动程序不同的功能,而且都不同的难度。本章主要讲解scul<BR>l0-3的内部结构;第5章,字符设备驱动程序的扩展操作,将介绍更复杂的设备:&nbsp;“一<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>l0-3的内部结构;第5章,字符设备驱动程序的扩展操作,将介绍更复杂的设备:&nbsp;“一<BR>个样例实现:scullpipe”介绍scullpipe,“设备文件的访问控制”介绍其他设备。<BR>&nbsp;<BR>主设备号和次设备号<BR>通过访问文件系统的名字(或“节点”)访问字符设备,通常这些文件位于/dev目录。<BR>设备文件是特殊文件,这一点可以通过ls&nbsp;-l输出的第一列中的“c”标明,它说明它们<BR>是字符节点。/dev下还有块设备,但它们的第一列是“b”;尽管如下介绍的某些内容也<BR>同样适用于块设备,现在我们只关注字符设备。如果你执行ls命令,在设备文件条目的<BR>最新修改日期前你会看到两个数(用逗号分隔),这个位置通常显示文件长度。这些数<BR>就是相应设备的主设备号和次设备号。下面的列表给出了我使用的系统上的一些设备。<BR>它们的主设备号是10,1和4,而次设备号是0,3,5,64-65和128-129。<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>主设备号标识设备对应的驱动程序。例如,/dev/null和/dev/zero都有驱动程序1管理,<BR>而所有的tty和pty都由驱动程序4管理。内核利用主设备号将设备与相应的驱动程序对应<BR>起来。<BR>&nbsp;<BR>次设备号只由设备驱动程序使用;内核的其他部分不使用它,仅将它传递给驱动程序。<BR>一个驱动程序控制若干个设备并不为奇(如上面的例子所示)――次顺便号提供了一种<BR>区分它们的方法。<BR>&nbsp;<BR>向系统增加一个驱动程序意味着要赋予它一个主设备号。这一赋值过程应该在驱动程序<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>向系统增加一个驱动程序意味着要赋予它一个主设备号。这一赋值过程应该在驱动程序<BR>(模块)的初始化过程中完成,它调用如下函数,这个函数定义在&lt;linux/fs.h&gt;:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>返回值是错误码。当出错时返回一个负值;成功时返回零或正值。参数major是所请求的<BR>主设备号,name是你的设备的名字,它将在/proc/devices中出现,fops是一个指向跳转<BR>表的指针,利用这个跳转表完成对设备函数的调用,本章稍后将在“文件操作”一节中<BR>介绍这些函数。<BR>&nbsp;<BR>主设备号是一个用来索引静态字符设备数组的整数。在1.2.13和早期的2.x内核中,这个<BR>数组有64项,而2.0.6到2.1.11的内核则升至128。由于只有设备才处理次设备号,regis<BR>ter_chrdev不传递次设备号。<BR>&nbsp;<BR>一旦设备已经注册到内核表中,无论何时操作与你的设备驱动程序的主设备号匹配的设<BR>备文件,内核都会通过在fops跳转表索引调用驱动程序中的正确函数。<BR>&nbsp;<BR>接下来的问题就是如何给程序一个它们可以请求你的设备驱动程序的名字。这个名字必<BR>须插入到/dev目录中,并与你的驱动程序的主设备号和次设备号相连。<BR>&nbsp;<BR>在文件系统上创建一个设备节点的命令是mknod,而且你必须是超级用户才能创建设备。<BR>除了要创建的节点名字外,该命令还带三个参数。例如,命令:<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>(代码)<BR>&nbsp;<BR>创建一个字符设备(c),主设备号是127,次设备号是0。由于历史原因,次设备号应该<BR>在0-255范围内,有时它们存储在一个字节中。存在很多原因扩展可使用的次设备号的范<BR>围,但就现在而言,仍然有8位限制。<BR>&nbsp;<BR>动态分配主设备号<BR>某些主设备号已经静态地分配给了大部分公用设备。在内核源码树的Documentation/dev<BR>ice.txt文件中可以找到这些设备的列表。由于许多数字已经分配了,为新设备选择一个<BR>唯一的号码是很困难的――不同的设备要不主设备号多得多。<BR>&nbsp;<BR>很幸运(或是感谢某些人天才),你可以动态分配主设备号了。如果你调用register_ch<BR>rdev时的major为零的话,这个函数就会选择一个空闲号码并做为返回值返回。主设备号<BR>总是正的,因此不会和错误码混淆。<BR>&nbsp;<BR>我强烈推荐你不要随便选择一个一个当前不用的设备号做为主设备号,而使用动态分配<BR>机制获取你的主设备号。<BR>&nbsp;<BR>动态分配的缺点是,由于分配给你的主设备号不能保证总是一样的,无法事先创建设备<BR>节点。然而这不是什么问题,这是因为一旦分配了设备号,你就可以从/proc/devices读<BR>到。为了加载一个设备驱动程序,对insmod的调用被替换为一个简单的脚本,它通过/pr<BR>oc/devices获得新分配的主设备号,并创建节点。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>oc/devices获得新分配的主设备号,并创建节点。<BR>&nbsp;<BR>/proc/devices一般如下所示:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>加载动态分配主设备号驱动程序的脚本可以利用象awk这类工具从/proc/devices中获取<BR>信息,并在/dev中创建文件。<BR>&nbsp;<BR>下面这个脚本,scull_load,是scull发行中的一部分。使用以模块形式发行的驱动程序<BR>的用户可以在/etc/rc.d/rc.local中调用这个脚本,或是在需要模块时手工调用。此外<BR>还有另一种方法:使用kerneld。这个方法和其他模块的高级功能将在第11章“Kerneld<BR>和高级模块化”中介绍。<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>这个脚本同样可以适用于其他驱动程序,只要重新定义变量和调整mknod那几行就可以了<BR>。上面那个脚本创建4个设备,4是scull源码中的默认值。<BR>&nbsp;<BR>脚本的最后两行看起来有点怪怪的:为什么要改变设备的组和权限呢?原因是这样的,<BR>由root创建的节点自然也属于root。默认权限位只允许root对其有写访问权,而其他只<BR>有读权限。正常情况下,设备节点需要不同的策略,因此需要进行某些修改。通常允许<BR>一组用户访问对设备,但实现细节却依赖于设备和系统管理员。安全是个大问题,这超<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>一组用户访问对设备,但实现细节却依赖于设备和系统管理员。安全是个大问题,这超<BR>出了本书的范围。scull_load中的chmod和chgrp那两行仅仅是最为处理权限问题的一点<BR>提示。稍后,在第5章的“设备文件的访问控制”一节中将介绍sculluid源码,展示设备<BR>驱动程序如何实现自己的设备访问授权。<BR>&nbsp;<BR>如果重复地创建和删除/dev节点似乎有点过分的话,有一个解决的方法。如果你看了内<BR>核源码fs/devices.c的话,你可以看到动态设备号是从127(或63)之后开始的,你可以<BR>用127做为主设备号创建一个长命节点,同时可以避免在每次相关设备加载时调用脚本。<BR>如果你使用了几个动态设备,或是新版本的内核改变了动态分配的特性,这个技巧就不<BR>能用了。(如果内核发生了修改,基于内核内部结构编写的代码并不能保证继续可以工<BR>作。)不管怎样,由于开发期间模块要不断地加载&nbsp;托对&nbsp;,你会发现这一技术在开发期<BR>间还是很有用的。<BR>&nbsp;<BR>就我看来,分配主设备号的最佳方式是,默认采用动态分配,同时留给你在加载时,甚<BR>至是编译时,指定主设备号的余地。使用我建议的代码将与自动端口探测的代码十分相<BR>似。scull的实现使用了一个全局变量,scull_major,来保存所选择的设备号。该变量<BR>的默认值是SCULL_MAJOR,在所发行的源码中为0,即“选择动态分配”。用户可以使用<BR>这个默认值或选择某个特定的主设备号,既可以在编译前修改宏定义,也可以在ins_mod<BR>命令行中指定。最后,通过使用scull_load脚本,用户可以在scull_load中命令行中将<BR>参数传递给insmod。<BR>&nbsp;<BR>这里是我在scull.c中使用的获取主设备号的代码:<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>(代码)<BR>&nbsp;<BR>从系统中删除设备驱动程序<BR>当从系统中卸载一个模块时,应该释放主设备号。这一操作可以在cleanup_module中调<BR>用如下函数完成:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>参数是要释放的主设备号和相应的设备名。内核对这个名字和设备号对应的名字进行比<BR>较:如果不同,返回-ENINVAL。如果主设备号超出了所允许的范围或是并未分配给这个<BR>设备,内核一样返回-EINVAL。在cleanup_module中注销资源失败会有非常不号的后果。<BR>下次读取/proc/devices时,由于其中一个name字串仍然指向模块内存,而那片内存已经<BR>不存在了,系统将产生一次失效。这种失效称为Oops*,内核在访问无效地址时将打印这<BR>样的消息。<BR>&nbsp;<BR>当你卸载驱动程序而又无法注销主设备号时,这种情况是无法恢复的,即便为此专门写<BR>一个“补救”模块也无济于事,因为unregister_chrdev中调用了strcmp,而strcmp将使<BR>用未映射的name字串,当释放设备时就会使系统Oops。无需说明,任何视图打开这个异<BR>常的设备号对应的设备的操作都会Oops。<BR>&nbsp;<BR>除了卸载模块,你还经常需要在卸载驱动程序时删除设备节点。如果设备节点是在加载<BR>时创建的,可以写一个简单的脚本在卸载时删除它们。对于我们的样例设备,脚本scull<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>时创建的,可以写一个简单的脚本在卸载时删除它们。对于我们的样例设备,脚本scull<BR>_unload完成这个工作。如果动态节点没有从/dev中删除,就会有可能造成不可预期的错<BR>误:如果动态分配的主设备号相同,开发者计算机上的一个空闲/dev/framegrabber就有<BR>可能在一个月后引用一个火警设备。“没有这个文件或目录”要比这个新设备所产生的<BR>后果要好得多。<BR>&nbsp;<BR>dev_t和kdev_t<BR>到目前为止,我们已经谈论了主设备号。现在是讨论次设备号和驱动程序如何使用次设<BR>备号来区分设备的时候了。<BR>&nbsp;<BR>每次内核调用一个设备驱动程序时,它都告诉驱动程序它正在操作哪个设备。主设备号<BR>和次设备号合在一起构成一个数据类型并用来标别某个设备。设备号的组合(主设备号<BR>和次设备号合在一起)驻留在稍后介绍的“inode”结构的i_rdev域中。每个驱动程序接<BR>收一个指向struct&nbsp;inode的指针做为第一个参数。这个指针通常也称为inode,函数可以<BR>通过查看inode-&gt;i_rdev分解出设备号。<BR>&nbsp;<BR>历史上,Unix使用dev_t保存设备号。dev_t通常是&lt;sys/types.h&gt;中定义的一个16位整数<BR>。而现在有时需要超过256个次设备号,但是由于有许多应用(包括C库在内)都了解dev<BR>_t的内部结构,改变dev_t是很困难的,如果改变dev_t的内部结构就会造成这些应用无<BR>法运行。因此,dev_t类型一直没有改变;它仍是一个16位整数,而且次设备号仍限制在<BR>0-255内。然而,在Linux内核内部却使用了一个新类型,kdev_t。对于每一个内核函数<BR>来说,这个新类型被设计为一个黑箱。它的想法是让用户程序不能了解kdev_t。如果kde<BR>v_t一直是隐藏的,它可以在内核的不同版本间任意变化,而不必修改每个人的设备驱动<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>v_t一直是隐藏的,它可以在内核的不同版本间任意变化,而不必修改每个人的设备驱动<BR>程序。<BR>&nbsp;<BR>有关kdev_t的信息被禁闭在&lt;linux/kdev_t.h&gt;中,其中大部分是注释。如果你对代码后<BR>的哲学感兴趣的话,这个头文件是一段很有指导性的代码。因为&lt;linux/fs.h&gt;已经包含<BR>了这个头文件,没有必要显式地包含这个文件。<BR>&nbsp;<BR>不幸的是,kdev_t类型是一个“现代”概念,在内核版本1.2中没有这个类型。在较新的<BR>内核中,所有的引用设备的内核变量和结构字段都是kdev_t的,但是在1.2.13中同样的<BR>变量却是dev_t的。如果你的驱动程序只使用它接收的结构字段,而不声明自己的变量的<BR>话,这不会有什么问题的。如果你需要声明自己的设备类型变量,为了可移植性你应该<BR>在你的头文件中加入如下几行:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>这段代码是样例源码中的sysdep.h头文件的一部分。我不会在源码中在引用dev_t,但是<BR>要假设前一个条件语句已经执行了。<BR>&nbsp;<BR>如下这些宏和函数是你可以对kdev_t执行的操作:<BR>&nbsp;<BR>MAJOR(kdev_t&nbsp;dev);<BR>&nbsp;<BR>从kdev_t结构中分解出主设备号。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>从kdev_t结构中分解出主设备号。<BR>&nbsp;<BR>MINOR(kdev_t&nbsp;dev);<BR>&nbsp;<BR>分解出次设备号。<BR>&nbsp;<BR>MKDEV(int&nbsp;ma,&nbsp;int&nbsp;mi);<BR>&nbsp;<BR>通过主设备号和次设备号返回kdev_t。<BR>&nbsp;<BR>kdev_t_to_nr(kdev_t&nbsp;dev);<BR>&nbsp;<BR>将kdev_t转换为一个整数(dev_t)。<BR>&nbsp;<BR>to_kdev_t(int&nbsp;dev);<BR>&nbsp;<BR>将一个整数转换为kdev_t。注意,核心态中没有定义dev_t,因此使用了int。<BR>&nbsp;<BR>与Linux&nbsp;1.2相关的头文件定义了同样的操作dev_t的函数,但没有那两个转换函数,这<BR>也就是为什么上面那个条件代码简单地将它们定义返回它们的参数值。<BR>&nbsp;<BR>文件操作<BR>在接下来的几节中,我们将看看驱动程序能够对它管理的设备能够完成哪些不同的操作<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>在接下来的几节中,我们将看看驱动程序能够对它管理的设备能够完成哪些不同的操作<BR>。在内核内部用一个file结构标别设备,而且内核使用file_operations结构访问驱动程<BR>序的函数。这一设计是我们所看到的Linux内核面向对象设计的第一个例证。我们将在以<BR>后看到更多的面向对象设计的例证。file_operations结构是一个定义在&lt;linux/fs.h&gt;中<BR>的数指针表。结构struct&nbsp;file将在以后介绍。<BR>&nbsp;<BR>我们已经register_chrdev调用中有一个参数是fops,它是一个指向一组操作(open,re<BR>ad等等)表的指针。这个表的每一个项都指向由驱动程序定义的处理相应请求的函数。<BR>对于你不支持的操作,该表可以包含NULL指针。对于不同函数的NULL指针,内核具体的<BR>处理行为是不同的,下一节将逐一介绍。<BR>&nbsp;<BR>随着新功能不断加入内核,file_operations结构已逐渐变得越来越大(尽管从1.2.0到2<BR>..0.x并没有增加新字段)。这种增长应该不会有什么副作用,因为在发现任何尺寸不匹<BR>配时,C编译器会将全局或静态struct变量中的未初始化字段填0。新的字段都加到结构<BR>的末尾*,所以在编译时会插入一个NULL指针,系统会选择默认行为(记住,对于所有模<BR>块需要加载的新内核,都要重新编译一次模块)。<BR>&nbsp;<BR>在2.1开发用内核中,有些与fops字段相关的函数原型发生了变化。这些变化将在第17章<BR>“近期发展”的“文件操作”一节中介绍。<BR>&nbsp;<BR>纵览不同操作<BR>下面的列表将介绍应用程序能够对设备调用的所有操作。这些操作通常称为“方法”,<BR>用面向对象的编程术语来说就是说明一个对象声明可以操作在自身的动作。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>用面向对象的编程术语来说就是说明一个对象声明可以操作在自身的动作。<BR>&nbsp;<BR>为了使这张列表可以用来当作索引,我尽量使它简洁,仅仅介绍每个操作的梗概以及当<BR>使用NULL时的内核默认行为。你可以在初次阅读时跳过这张列表,以后再来查阅。<BR>&nbsp;<BR>在介绍完另一个重要数据结构(file)后,本章的其余部分将讲解最重要的一些操作并<BR>提供一些提示,告诫和真实的代码样例。由于我们尚不能深入探讨内存管理和异步触发<BR>机制,我们将在以后的章节中介绍这些更为复杂操作。<BR>&nbsp;<BR>struct&nbsp;file_operations中的操作按如下顺序出现,除非注明,它们的返回0时表示成功<BR>,发生错误时返回一个负的错误编码:<BR>&nbsp;<BR>int&nbsp;(*lseek)(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;off_t,&nbsp;int);<BR>&nbsp;<BR>方法lseek用来修改一个文件的当前读写位置,并将新位置做为(正的)返回值返回。出<BR>错时返回一个负的返回值。如果驱动程序没有设置这个函数,相对与文件尾的定位操作<BR>失败,其他定位操作修改file结构(在“file结构”中介绍)中的位置计数器,并成功<BR>返回。2.1.0中该函数的原型发生了变化,第17章“原型变化”将讲解这些内容。<BR>&nbsp;<BR>int&nbsp;(*read)(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;char&nbsp;*,&nbsp;int);<BR>&nbsp;<BR>用来从设备中读取数据。当其为NULL指针时将引起read系统调用返回-EINVAL(“非法参<BR>数”)。函数返回一个非负值表示成功的读取了多少字节。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>数”)。函数返回一个非负值表示成功的读取了多少字节。<BR>&nbsp;<BR>int&nbsp;(*write)(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;const&nbsp;char&nbsp;*,&nbsp;int);<BR>&nbsp;<BR>向设备发送数据。如果没有这个函数,write系统调用向调用程序返回一个-EINVAL。注<BR>意,版本1.2的头文件中没有const这个说明符。如果你自己在write方法中加入了const<BR>,当与旧头文件编译时会产生一个警告。如果你没有包含const,新版本的内核也会产生<BR>一个警告;在这两种情况你都可以简单地忽略这些警告。如果返回值非负,它就表示成<BR>功地写入的字节数。<BR>&nbsp;<BR>int&nbsp;(*readdir)(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;void&nbsp;*,&nbsp;filldir_t);<BR>&nbsp;<BR>对于设备节点来说,这个字段应该为NULL;它仅用于目录。<BR>&nbsp;<BR>int&nbsp;(*select)(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;int,&nbsp;select_table&nbsp;*);<BR>&nbsp;<BR>select一般用于程序询问设备是否可读和可写,或是否一个“异常”条件发生了。如果<BR>指针为NULL,系统假设设备总是可读和可写的,而且没有异常需要处理。“异常”的具<BR>体含义是和设备相关的。在当前的2.1开发用内核中,select的实现方法完全不同。(见<BR>第17章的“poll方法”)。返回值告诉系统条件满足(1)或不满足(0)。<BR>&nbsp;<BR>int&nbsp;(*ioctl)(struct&nbsp;inode&nbsp;*,&nbsp;struct&nbsp;file&nbsp;*,&nbsp;unsigned&nbsp;int,&nbsp;unsigned&nbsp;long);<BR>&nbsp;<BR></P></FONT><FONT 

⌨️ 快捷键说明

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