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

📄 (ldd) ch05-字符设备驱动程序的扩展操作.htm

📁 html格式
💻 HTM
📖 第 1 页 / 共 4 页
字号:
            <DIV align=center><FONT color=#ffffff>|</FONT></DIV></TD>
          <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/6.htm"><FONT color=#ffffff 
      size=2>上一页</FONT></A> | <A href="http://joyfire.net/lsdp/8.htm"><FONT 
      color=#ffffff size=2>下一页</FONT></A></P>
      <P align=center><FONT face=黑体 color=#ffffff size=6>(LDD) 
      Ch05-字符设备驱动程序的扩展操作</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;Ch05-字符设备驱动程序的扩展操作(转载)<BR>发信站:&nbsp;饮水思源&nbsp;(2001年12月13日08:57:16&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>第5章&nbsp;字符设备驱动程序的扩展操作<BR>&nbsp;<BR>&nbsp;<BR>在关于字符设备驱动程序的那一章中,我们构建了一个完整的设备驱动程序,从中用户<BR>可以读也可以写。但实际一个驱动程序通常会提供比同步read和write更多的功能。现在<BR>如果出了什么毛病,我已经配备了调试工具,我们可以大胆的实验并实现新操作。<BR>&nbsp;<BR>通过补充设备读写操作的功能之一就是控制硬件,最常用的通过设备驱动程序完成控制<BR>动作的方法就是实现ioctl方法。另一种方法是检查写到设备中的数据流,使用特殊序列<BR>做为控制命令。尽管有时也使用后者,但应该尽量避免这样使用。不过稍后我们还是会<BR>在本章的“非ioctl设备控制”一节中介绍这项技术。<BR>&nbsp;<BR>正如我在前一章中所猜想的,ioctl系统调用为驱动程序执行“命令”提供了一个设备相<BR>关的入口点。与read和其他方法不同,ioctl是设备相关的,它允许应用程序访问被驱动<BR>硬件的特殊功能――配置设备以及进入或退出操作模式。这些“控制操作”通常无法通<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>硬件的特殊功能――配置设备以及进入或退出操作模式。这些“控制操作”通常无法通<BR>过read/write文件操作完成。例如,你向串口写的所有数据都通过串口发送出去了,你<BR>无法通过写设备改变波特率。这就是ioctl所要做的:控制I/O通道。<BR>&nbsp;<BR>实际设备(与scull不同)的另一个重要功能是,读或写的数据需要同其他硬件交互,需<BR>要某些同步机制。阻塞型I/O和异步触发的概念将满足这些需求,本章将通过一个改写的<BR>scull设备介绍这些内容。驱动程序利用不同进程间的交互产生异步事件。与最初的scul<BR>l相同,你无需特殊硬件来测试驱动程序是否可以工作。直到第8章“硬件管理”我才会<BR>真正去与硬件打交道。<BR>&nbsp;<BR>ioctl<BR>在用户空间内调用ioctl函数的原型大致如下:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>由于使用了一连串的“.”的缘故,该原型在Unix系统调用列表之中非常突出,这些点代<BR>表可变数目参数。但是在实际系统中,系统调用实际上不会有可变数目个参数。因为用<BR>户程序只能通过第2章“编写和运行模块”的“用户空间和内核空间”一节中介绍的硬件<BR>“门”才能访问内核,系统调用必须有精确定义的参数个数。因此,ioctl的第3个参数<BR>事实上只是一个可选参数,这里用点只是为了在编译时防止编译器进行类型检查。第3个<BR>参数的具体情况与要完成的控制命令(第2个参数)有关。某些命令不需要参数,某些需<BR>要一个整数做参数,而某些则需要一个指针做参数。使用指针通常是可以用来向ioctl传<BR>递任意数目数据;设备可以从用户空间接收任意大小的数据。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>递任意数目数据;设备可以从用户空间接收任意大小的数据。<BR>&nbsp;<BR>系统调用的参数根据方法的声明传递给驱动程序方法:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>inode和filp指针是根据应用程序传递的文件描述符fd计算而得的,与read和write的用<BR>法一致。参数cmd不经修改地传递给驱动程序,可选的arg参数无论是指针还是整数值,<BR>它都以unsigned&nbsp;long的形式传递给驱动程序。如果调用程序没有传递第3个参数,驱动<BR>程序所接收的arg没有任何意义。<BR>&nbsp;<BR>由于附加参数的类型检查被关闭了,如果非法参数传递给ioctl,编译器无法向你报警,<BR>程序员在运行前是无法注意这个错误的。这是我所见到的ioctl语义方面的唯一一个问题<BR>。<BR>&nbsp;<BR>如你所想,大多数ioctl实现都包括一个switch语句来根据cmd参数选择正确的操作。不<BR>同的命令对应不同的数值,为了简化代码我们通常会使用符号名代替数值。这些符号名<BR>都是在预处理中赋值的。不同的驱动程序通常会在它们的头文件中声明这些符号;scull<BR>就在scull.h中声明了这些符号。<BR>&nbsp;<BR>选择ioctl命令<BR>在编写ioctl代码之前,你需要选择对应不同命令的命令号。遗憾的是,简单地从1开始<BR>选择号码是不能奏效的。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>选择号码是不能奏效的。<BR>&nbsp;<BR>为了防止对错误的设备使用正确的命令,命令号应该在系统范围内是唯一的。这种失配<BR>并不是不很容易发生,程序可能发现自己正在对象FIFO和kmouse这类非串口输入流修改<BR>波特率。如果每一个ioctl命令都是唯一的,应用程序就会获得一个EINVAL错误,而不是<BR>无意间成功地完成了操作。<BR>&nbsp;<BR>为了达到唯一性的目的,每一个命令号都应该由多个位字段组成。Linux的第一版使用了<BR>一个16位整数:高8位是与设备相关的“幻”数,低8位是一个序列号码,在设备内是唯<BR>一的。这是因为,用Linus的话说,他有点“无头绪”,后来才接收了一个更好的位字段<BR>分割方案。遗憾的是,很少有驱动程序使用新的约定,这就挫伤了程序员使用新约定的<BR>热情。在我的源码中,为了发掘这种约定都提供了那些功能,同时防止被其他开发人员<BR>当成异教徒而禁止,我使用了新的定义命令的方法。<BR>&nbsp;<BR>为了给我的驱动程序选择ioctl号,你应该首先看看include/asm/ioctl.h和Documentati<BR>on/ioctl-number.txt这两个文件。头文件定义了位字段:类型(幻数),基数,传送方<BR>向,参数的尺寸等等。ioctl-number.txt文件中罗列了在内核中使用的幻数。这个文件<BR>的新版本(2.0以及后继内核)也给出了为什么应该使用这个约定的原因。<BR>&nbsp;<BR>很不幸,在1.2.x中发行的头文件没有给出切分ioctl位字段宏的全集。如果你需要象我<BR>的scull一样使用这种新方法,同时还要保持向后兼容性,你使用scull/sysdep.h中的若<BR>干代码行,我在那里给出了解决问题的文档的代码。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>现在已经不赞成使用的选择ioctl号码的旧方法非常简单:选择一个8位幻数,比如“k”<BR>(十六进制为0x6b),然后加上一个基数,就象这样:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>如果应用程序和驱动程序都使用了相同的号码,你只要在驱动程序里实现switch语句就<BR>可以了。但是,这种在传统Unix中有基础的定义ioctl号码的方法,不应该再在新约定中<BR>使用。这里我介绍就方法只是想给你看看一个ioctl号码大致是个什么样子的。<BR>&nbsp;<BR>新的定义号码的方法使用了4个位字段,它们有如下意义。下面我所介绍的新符号都定义<BR>在&lt;linux/ioctl.h&gt;中。<BR>&nbsp;<BR>类型<BR>&nbsp;<BR>幻数。选择一个号码,并在整个驱动程序中使用这个号码。这个字段有8位宽(_IOC_TYP<BR>EBITS)。<BR>&nbsp;<BR>号码<BR>&nbsp;<BR>基(序列)数。它也是8位宽(_IOC_NRBITS)。<BR>&nbsp;<BR>方向<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>方向<BR>&nbsp;<BR>如果该命令有数据传输,它定义数据传输的方向。可以使用的值有,_IOC_NONE(没有数<BR>据传输),_IOC_READ,_IOC_WRITE和_IOC_READ&nbsp;|&nbsp;_IOC_WRITE(双向传输数据)。数据<BR>传输是从应用程序的角度看的;IOC_READ意味着从设备中读数据,驱动程序必须向用户<BR>空间写数据。注意,该字段是一个位屏蔽码,因此可以用逻辑AND操作从中分解出_IOC_R<BR>EAD和_IOC_WRITE。<BR>&nbsp;<BR>尺寸<BR>&nbsp;<BR>所涉及的数据大小。这个字段的宽度与体系结构有关,当前的范围从8位到14位不等。你<BR>可以在宏_IOC_SIZEBITS中找到某种体系结构的具体数值。不过,如果你想要你的驱动程<BR>序可移植,你只能认为最大尺寸可达255个字节。系统并不强制你使用这个字段。如果你<BR>需要更大尺度的数据传输,你可以忽略这个字段。下面我们将介绍如何使用这个字段。<BR>&nbsp;<BR>包含在&lt;linux/ioctl.h&gt;之中的头文件&lt;asm/ioctl.h&gt;定义了可以用来构造命令号码的宏<BR>:_IO(type,nr),_IOR(type,nr,size),_IOW(type,nr,size)和IOWR(type,nr,size)。<BR>每一个宏都对应一种可能的数据传输方向,其他字段通过参数传递。头文件还定义了解<BR>码宏:_IOC_DIR(nr),_IOC_TYPE(nr),_IOC_NR(nr)和_IOC_SIZE(nr)。我不打算详细介<BR>绍这些宏,头文件里的定义已经足够清楚了,本节稍后会给出样例。<BR>&nbsp;<BR>这里是scull中如果定义ioctl命令的。特别地,这些命令设置并获取驱动程序的配置参<BR>数。在标准的宏定义中,要传送的数据项的尺寸有数据项自身的实例代表,而不是sizeo<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>数。在标准的宏定义中,要传送的数据项的尺寸有数据项自身的实例代表,而不是sizeo<BR>f(item),这是因为sizeof是宏扩展后的一部分。<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>最后一条命令,HARDRESET,用来将模块使用计数器复位为0,这样就可以在计数器发生<BR>错误时就可以卸载模块了。实际的源码定义了从IOCHQSET到HARDRESET间的所有命令,但<BR>这里没有列出。<BR>&nbsp;<BR>我选择用两种方法实现整数参数传&nbsp;莰D―通过指针和显式数值,尽管根据已有的约定,i<BR>octl应该使用指针完成数据交换。同样,这两种方法还用于返回整数:通过指针和设置<BR>返回值。如果返回值是正的,这就可以工作;对与任何一个系统调用的返回值,正值是<BR>受保护的(如我们在read和write所见到的),而负值则被认为是一个错误值,用其设置<BR>用户空间中的errno变量。<BR>&nbsp;<BR>“交换”和“移位”操作并不专用于scull设备。我实现“交换”操作是为了给出“方向<BR>”字段的所有可能值,而“移位”操作则将“告知”和“查询”操作组合在一起。某些<BR>时候是需要原子性*测试兼设置这类操作的――特别是当应用程序需要加锁和解锁时。<BR>&nbsp;<BR>显式的命令基数没有什么特殊意义。它只是用来区分命令的。事实上,由于ioctl号码的<BR>“方向”为会有所不同,你甚至可以在读命令和写命令中使用同一个基数。我选择除了<BR>在声明中使用基数外,其他地方都不使用它,这样我就不必为符号值赋值了。这也是为<BR>什么显式的号码出现在上面的定义中。我只是向你介绍一种使用命令号码的方法,你可<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>什么显式的号码出现在上面的定义中。我只是向你介绍一种使用命令号码的方法,你可<BR>以自由地采用不同的方法使用它。<BR>&nbsp;<BR>当前,参数cmd的值内核并没有使用,而且以后也不可能使用。因此,如果你想偷懒,你<BR>可以省去上面那些复杂的声明,而直接显式地使用一组16位数值。但另一方面,如果你<BR>这样做了,你就无法从使用位字段中受益了。头文件&lt;linux/kd.h&gt;就是这种旧风格方法<BR>的例子,但是它们并不是因为偷懒才这样做的。修改这个文件需要重新编译许多应用程<BR>序。<BR>&nbsp;<BR>返回值<BR>ioctl的实现通常就是根据命令号码的一个switch语句。但是,当命令号码不能匹配任何<BR>一个合法操作时,default选择使用是什么?这个问题是很有争议性的。大多数内核函数<BR>返回-EINVAL(“非法参数”),这是由于命令参数确实不是一个合法的参数,这样做是<BR>合适的。然而,POSIX标准上说,如果调用了一个不合适的ioctl命令,应该返回-ENOTTY<BR>。对应的消息是“不是终端”――这不是用户所期望的。你不得不决定是严格依从标准<BR>还是一般常识。我们将本章的后面介绍为什么依从POSIX标准需要返回ENOTTY。<BR>&nbsp;<BR>预定义命令<BR>尽管ioctl系统调用大部分都用于操作设备,但还有一些命令是由内核识别的。注意,这<BR>些命令是在你自己的文件操作前调用的,所以如果你选择了和它们相同的命令号码,你<BR>将无法接收到那个命令的请求,而且由于ioctl命令的不唯一性,应用程序会请求一些未<BR>可知的请求。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>预定义命令分为3组:用于任何文件(普通,设备,FIFO和套接字文件)的,仅用于普通<BR>文件的以及和文件系统相关的;最后一组命令只能在宿主机文件系统上执行(间chattr<BR>命令)。设备驱动程序编写者仅对第1组感兴趣就可以了,它们的幻数是“T”。分析其<BR>他组的工作将留做读者的练习;ext2_ioctl是其中最有意思的函数(尽管比你想象的要<BR>容易得多),它实现了只追加标志和不可变标志。<BR>&nbsp;<BR>下列ioctl命令对任何文件都是预定义的:<BR>&nbsp;<BR>FIOCLEX<BR>&nbsp;<BR>设置exec时关闭标志(File&nbsp;IOctl&nbsp;Close&nbsp;on&nbsp;EXec)。<BR>&nbsp;<BR>FIONCLEX<BR>&nbsp;<BR>清除exec时关闭标志。<BR>&nbsp;<BR>FIOASYNC<BR>&nbsp;<BR>设置或复位文件的同步写。Linux中没有实现同步写;但这个调用存在,这样请求同步写<BR>的应用程序就可以编译和运行了。如果你不知道同步写是怎么回事,你也不用费神去了<BR>解它了:你不需要它。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>FIONBIO<BR>&nbsp;<BR>“File&nbsp;IOctl&nbsp;Nonblocking&nbsp;I/O(文件ioctl非阻塞型I/O)”(稍后在“阻塞型和非阻<BR>塞型操作”一节中介绍)。该调用修改filp-&gt;f_flags中的O_NONBLOCK标志。传递给系统<BR>调用的第3个参数用来表明该标志是设置还是清除。我们将在本章后面谈到它的作用。注<BR>意,fcntl系统调用使用F_SETFL命令也可以修改这个标志。<BR>&nbsp;<BR>列表中的最后一项引入了一个新系统调用fcntl,它看起来和ioctl很象。事实上,fcntl<BR>调用与ioctl非常相似,它也有一个命令参数和额外(可选的)一个参数。它和ioctl分<BR>开主要是由于历史原因:当Unix开发人员面对“控制”I/O操作的问题时,他们决定文件<BR>和设备应该是不同的。那时,唯一的设备是终端,这也就解释了为什么-ENOTTY是标准的<BR>非法ioctl命令的返回值。这个问题是是否保持向后兼容性的老问题。<BR>&nbsp;<BR>使用ioctl参数<BR>我们需要讲解的最后一点是,在分析scull驱动程序的ioctl代码前,首先弄明白如何使<BR>用那个额外的参数。如果它是一个整数就非常简单了:可以直接使用它。如果它是一个<BR>指针,就必须注意一些问题了。<BR>&nbsp;<BR>当用一个指针引用用户空间时,我们首先要确保它指向了合法的用户空间,并且对应页<BR>面当前恰在映射中。如果内核代码试图访问范围之外的地址,处理器就会发出一个异常<BR>。内核代码中的异常将由上至2.0.x的内核转换为oops消息。设备驱动程序应该通过验证<BR>将要访问的用户地址空间的合法性来防止这种失效的发生,如果地址是非法的应该返回<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>将要访问的用户地址空间的合法性来防止这种失效的发生,如果地址是非法的应该返回<BR>一个错误码。<BR>&nbsp;<BR>Linux&nbsp;2.1中引入新功能之一就是内核代码的异常处理。遗憾的是,正确的实现需要驱动<BR>程序-内核接口的较大改动。本章给出的非法只适用于旧内核,从1.2.13到2.0.x。新接<BR>口将在第17章“近期发展”的“处理内核空间失效”一节中介绍,那里给出的例子通过<BR>某些预处理宏将使支持的内核扩展到2.1.43。<BR>&nbsp;<BR>内核1.x.y和2.0.x的地址验证是通过函数verify_area实现的,它的原型定义在&lt;linux/m<BR>m.h&gt;中:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>第一个参数应该是VERIFY_READ或VERIFY_WRITE,这取决于你要在内存区上完成读还是写<BR>操作。ptr参数是一个用户空间地址,extent是一个字节计数。例如,如果ioctl需要从<BR>用户空间读一个整数,extent就是sizeof(int)。如果在指定的地址上进行读和写操作,<BR>使用VERIFY_WRITE,它是VERIFY_READ的超集。<BR>&nbsp;<BR>验证读只检查地址是否是合法的:除此之外,验证写要好检查只读和copy-on-write页面<BR>。copy-on-write页面一个共享可写页面,它还没有被任何共享进程写过;当你验证写时<BR>,verify_area完成“复制兼完成可写配置”操作。很有意思的是,这里无需检查页面是<BR>否“在”内存中,这是由于合法页面将由失效函数正确地进行处理,甚至从内核代码中<BR>调用也可以。我们已经在第3章“字符设备”的“Scull的内存使用”一节中看到内核代<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>调用也可以。我们已经在第3章“字符设备”的“Scull的内存使用”一节中看到内核代<BR>码可以成功地完成页面失效处理。<BR>&nbsp;<BR>象大多数函数一样,verify_area返回一个整数值:0意味着成功,负值代表一个错误,<BR>应该将这个错误返回给调用者。<BR>&nbsp;<BR>scull源码在switch之前分析ioctl号码的各个位字段:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>在调用verify_area之后,再有驱动程序完成真正的数据传送。除了memcpy_tofs和memcp<BR>y_fromfs函数外,程序员还可以使用两个专为常用数据尺寸(1,2和4个字节,在以及64<BR>位平台上的8个字节)优化的函数。这些函数定义在&lt;asm/segment.h&gt;中。<BR>&nbsp;<BR>put_user(datum,&nbsp;ptr)<BR>&nbsp;<BR>实际上它是一个最终调用__put_user的宏;编译时将其扩展为一条机器指令。驱动程序<BR>应该尽可能使用put_user,而不是memcpy_tofs。由于在宏表达式中不进行类型检查,你<BR>可以传递给put_user任何类型的数据指针,不过它应该是一个用户空间地址。数据传输<BR>的尺寸依赖于ptr参数的类型,这是在编译时通过特殊的gcc伪函数实现的,这里没有介<BR>绍的必要。结果,如果ptr是一个字符指针,就传递1个字节,依此类推分别有2,4和8个<BR>字节。如果被指引的数据不是所支持的尺寸,被编译的代码就会调用函数bad_user_acce<BR>ss_length。如果这些编译代码是一个模块,由于这个符号没有开放,模块就不能加载了<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>ss_length。如果这些编译代码是一个模块,由于这个符号没有开放,模块就不能加载了<BR>。<BR>&nbsp;<BR>get_user(ptr)<BR>&nbsp;<BR>这个宏用来从用户空间获取一个数据。除了数据传输的方向不同外,它与put_user是一<BR>样的。<BR>&nbsp;<BR>当insmod不能解析符号时,bad_user_access_length的又臭又长的名字可以当作一个很<BR>有意义的错误信息。这样,开发人员就可以在向大众分布模块前加载和测试模块,他会<BR>很快找到并修改错误。相反,如果使用了不正确尺寸的put_user和get_user直接编译到<BR>了内核中,bad_user_access_length就会导致系统panic。尽管对于尺寸错误的数据传输<BR>来说,oops比其系统panic要友好得多,但还是选择了较为激进的方法来尽力杜绝这种错<BR>误。<BR>&nbsp;<BR>scull的ioctl实现只传送设备的可配置参数,其代码非常简单,罗列如下:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>还有6项是操作scull_qset的。这些操作scull_quantum的一样,为了节省空间,没有在<BR>上面的例子中列出。<BR>&nbsp;<BR>从调用者的角度看(即从用户空间),传递和接收参数的6种方法如下所示:<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>从调用者的角度看(即从用户空间),传递和接收参数的6种方法如下所示:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>如果你需要写一个可以在Linux&nbsp;1.2里运行的模块,get_user和put_user会是非常棘手的<BR>函数,因为它们直到内核1.3才引入到系统中。在切换到类型依赖宏之前,程序员使用一<BR>些称为get_user_byte等等的函数。旧的宏只在内核1.3中定义了,在2.0内核中,只有你<BR>事先使用了#define&nbsp;WE_REALLY_WANT_TO_USE_A_BROKEN_INTERFACE时才能使用旧的宏。<BR>不过为了可移植性,为旧内核定义put_user是一种更好的解决方法,于是为了驱动程序<BR>可以在旧内核中良好运行,scull/sydep.h包含了这些宏的定义。<BR>&nbsp;<BR>非ioctl设备控制<BR>有时通过向设备自身发送写序列能够更好地完成对设备的控制。例如,这一技术使用在<BR>控制台驱动程序中,它称为“escape序列”,用来控制光标移动,改变默认颜色,或是<BR>完成某些配置任务。用这种方法实现设备控制的好处是,用户仅用写数据就可以完成对<BR>设备的控制,无需使用(有时是写)完成设备配置的程序。<BR>&nbsp;<BR>例如,程序setterm通过打印escape序列完成对控制台(或其他终端)的配置。这种方法<BR>的优点是可以远程控制设备。由于可以简单地重定向数据流完成配置工作,控制程序可<BR>以运行在另外一台不同的计算机上,而不一定非要在被控设备的计算机上。你已经在终<BR>端上使用了这项技术,但这项技术可以更通用一些。<BR>&nbsp;<BR>“通过打印控制”的缺点是,它给设备增加了策略限制;例如,只有你确认控制序列不<BR></P></FONT><FONT 

⌨️ 快捷键说明

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