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

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

📁 Linux设备驱动程序用于开发LINUX
💻 HTM
📖 第 1 页 / 共 4 页
字号:
      color=#ffffff size=3>
      <P>“通过打印控制”的缺点是,它给设备增加了策略限制;例如,只有你确认控制序列不<BR>会出现在正常写到设备的数据中时,这项技术才是可用的。对于终端来说,这只能说是<BR>部分正确。尽管文本显示只意味着显示ASCII字符,但有时控制字符也会出现在正在打印<BR>的数据中,因此会影响控制台的配置。例如,当你对二进制文件进行grep时可能会发生<BR>这样的情况;分解出的字符行可能什么都包含,最后经常会造成控制台的字体错误*。<BR>&nbsp;<BR>写控制特别适合这样的设备,不传输数据,仅相应命令,如机器人设备。<BR>&nbsp;<BR>例如,我所写的驱动程序之一是控制一个在两个轴上的摄像头的移动。在这个驱动程序<BR>中,“设备”是一对旧的步进马达,它既不能读也不能写。向步进马达“发送数据流”<BR>多少没有多大意义。在这种情况下,驱动程序将所写的数据解释为ASCII命令,并将请求<BR>转换为脉冲,实现对步进马达的控制。命令可以是任何象“向左移动14步”,“达到位<BR>置100,43”或“降低默认速度”之类的字串。驱动程序仅将/dev中的设备节点当作为应<BR>用程序设立的命令通道。对该设备直接控制的优点是,你可以使用cat来移动摄像头,而<BR>无需写并编译发出ioctl调用的特殊代码。<BR>&nbsp;<BR>当编写“面向命令的”驱动程序时,没有理由要实现ioctl方法。为解释器多实现一条命<BR>令对于实现和使用来说,都更容易。<BR>&nbsp;<BR>好奇的读者可以看看由O’Reilly&nbsp;FTP站点提供的源码stepper目录中的stepper驱动程序<BR>;由于我认为代码没有太大的意义(而且质量也不是太高),这里没有包含它。<BR>&nbsp;<BR>阻塞型I/O<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>阻塞型I/O<BR>read的一个问题是,当尚未有数据可读,而又没有到文件尾时如何处理。<BR>&nbsp;<BR>默认的回答是,“我们必须睡眠等待数据。”本节将介绍进程如何睡眠,如何唤醒,以<BR>及一个应用程序如何在不阻塞read调用的情况下,查看是否有数据。对于写来说也可以<BR>适用同样的方法。<BR>&nbsp;<BR>通常,在我向你介绍真实的代码前,我将解释若干概念。<BR>&nbsp;<BR>睡眠和唤醒<BR>当进程等待事件(可以是输入数据,子进程的终止或是其他什么)时,它需要进入睡眠<BR>状态以便其他进程可以使用计算资源。你可以调用如下函数之一让进程进入睡眠状态:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>然后用如下函数之一唤醒进程:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>在前面的函数中,wait_queue指针的指针用来代表事件;我们将在“等待队列”一节中<BR>详细讨论这个数据结构。从现在开始,唤醒进程需要使用进程睡眠时使用的同一个队列<BR>。因此,你需要为每一个可能阻塞进程的事件对应一个等待队列。如果你管理4个设备,<BR>你需要为阻塞读预备4个等待队列,为阻塞写再预备4个。存放这些队列的最佳位置是与<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>你需要为阻塞读预备4个等待队列,为阻塞写再预备4个。存放这些队列的最佳位置是与<BR>每个设备相关的硬件数据结构(在我们的例子中就是Scull_Dev)。<BR>&nbsp;<BR>但“可中断”调用和普通调用有什么区别呢?<BR>&nbsp;<BR>sleep_on不能信号取消,但interruptible_sleep_on可以。其实,仅在内核的临界区才<BR>调用sleep_on;例如,当等待从磁盘上读取交换页面时。没有这些页面进程就无法继续<BR>运行,用信号打断这个操作是没有任何意义的。然而,在所谓“长系统调用”,如read<BR>,中要使用interruptible_sleep_on。当进程正等待键盘输入时,用一个信号将进程杀<BR>死是很有意义的。<BR>&nbsp;<BR>类似地,wake_up唤醒睡在队列上的任何一个进程,而wake_up_interruptible仅唤醒可<BR>中断进程。<BR>&nbsp;<BR>做为一个驱动程序编写人员,由于进程仅在read或write期间才睡眠在驱动程序代码上,<BR>你应该调用interruptible_sleep_on和wake_up_interruptible。不过,事实上由于没有<BR>“不可中断”的进程在你的队列上睡眠,你也可以调用wake_up。但是,出于源代码一致<BR>性的考虑,最好不这样做。(此外,wake_up比它的搭档来说要稍微慢一点。)<BR>&nbsp;<BR>编写可重入的代码<BR>当进程睡眠后,驱动程序仍然活着,而且可以由另一个进程调用。让我们一控制台驱动<BR>程序为例。当一个应用在tty1上等待键盘输入,用户切换到tty2上并派生了一个新的外<BR>壳。现在,两个外壳都在控制台驱动程序中等待键盘输入,但它们睡在不同的队列上:<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>壳。现在,两个外壳都在控制台驱动程序中等待键盘输入,但它们睡在不同的队列上:<BR>一个睡在与tty1相关的队列上,一个睡在与tty2相关的队列上。每个进程都阻塞在inter<BR>ruptible_sleep_on函数中,但驱动程序让可以继续接收和响应其他tty的请求。<BR>&nbsp;<BR>可以通过编写“可重入代码”轻松地处理这种情况。可重入代码是不在全局变量中保留<BR>状态信息的代码,因此能够管理交织在一起的调用,而不会将它们混淆起来。如果所有<BR>的状态信息都与进程有关,就不会发生相互干扰。<BR>&nbsp;<BR>如果需要状态信息,既可以在驱动程序函数的局部变量中保存(每个进程都有不同的堆<BR>栈来保存局部变量),也可以保存在访问文件用的filp中的private_data中。由于同一<BR>个filp可能在两个进程间共享(通常是父子进程),最好使用局部变量*。<BR>&nbsp;<BR>如果你需要保存大规模的状态信息,你可以将指针保存在局部变量中,并用kmalloc获取<BR>实际存储空间。此时,你千万别忘了kfree这些数据,因为当你在内核空间工作时,没有<BR>“在进程终止时释放所有资源”的说法。<BR>&nbsp;<BR>你需要将所有调用了sleep_on(或是schedule)的函数写成可重入的,并且包括所有在<BR>这个函数调用轨迹中的所有函数。如果sample_read调用了sample_getdata,后者可能会<BR>阻塞,由于调用它们的进程睡眠后无法阻止另一个进程调用这些函数,sample_read和sa<BR>mple_gendata都必须是可重入的。此外,任何在用户空间和内核空间复制数据的函数也<BR>必须是可重入的,这是因为访问用户空间可能会产生页面失效,当内核处理失效页面时<BR>,进程可以会进入睡眠状态。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>等待队列<BR>我听见你在问的下一个问题是,“我到底如何使用等待队列呢?”<BR>&nbsp;<BR>等待队列很容易使用,尽管它的设计很是微妙,但你不需要直到它的内部细节。处理等<BR>待队列的最佳方式就是依照如下操作:<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;声明一个struct&nbsp;wait_queue&nbsp;*变量。你需要为每一个可以让进程睡眠的事件<BR>预备这样一个变量。这就是我建议你放在描述硬件特性数据结构中的数据项。<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将该变量的指针做为参数传递给不同的sleep_on和wake_up函数。<BR>&nbsp;<BR>这相当容易。例如,让我们想象一下,当进程读你的设备时,你要让这个进程睡眠,然<BR>后在某人向设备写数据后唤醒这个进程。下面的代码就可以完成这些工作:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>该设备的这段代码就是例子程序中的sleepy,象往常一样,可以用cat或输入/输出重定<BR>向等方法测试它。<BR>&nbsp;<BR>上面列出的两个操作是你唯一操作在等待队列上的两个操作。不过,我知道某些读者对<BR>它的内部结构感兴趣,但通过源码掌握它的内部结构很困难。如果你不对更多的细节感<BR>兴趣,你可以跳过下一节,你不会损失什么的。注意,我谈论的是“当前”实现(版本2<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>兴趣,你可以跳过下一节,你不会损失什么的。注意,我谈论的是“当前”实现(版本2<BR>..0.x),但没有什么规定限制内核开发人员必须依照那样的实现。如果出现了更好的实<BR>现,内核很容易就会使用新的,由于驱动程序编写人员只能通过那两个合法操作使用等<BR>待队列,对他们来说没有什么坏的影响。<BR>&nbsp;<BR>当前struct&nbsp;wait_queue额实现使用了两个字段:一个指向struct&nbsp;task_struct结构(等<BR>待进程)的指针,和一个指向struct&nbsp;wait_queue(链表中的下一个结构)的指针。等待<BR>队列是循环链表,最后一个结构指向第一个结构。<BR>&nbsp;<BR>该设计的引入注目的特点是,驱动程序编写人员从来不声明或使用这个结构;他们仅仅<BR>传递它的指针或指针的指针。实际的结构是存在的,但只在一个地方:在__sleep_on函<BR>数的局部变量中,上面介绍的两个sleep_on函数最终会调用这个函数。<BR>&nbsp;<BR>这看上去有点奇怪,不过这是一个非常明智的选择,因为无需处理这种结构的分配和释<BR>放。进程每次睡在某个队列上,描述其睡眠的数据结构驻留在进程对应的不对换的堆栈<BR>页中。<BR>&nbsp;<BR>当进程加入或从队列中删除时,实际的操作如图5-1所示。<BR>&nbsp;<BR>(图5-1&nbsp;等待队列的工作示意)<BR>&nbsp;<BR>阻塞型和非阻塞型操作<BR>在分析功能完整的read和write方法前,我们还需要看看另外一个问题,这就是filp-&gt;f_<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>在分析功能完整的read和write方法前,我们还需要看看另外一个问题,这就是filp-&gt;f_<BR>flags中的O_NONBLOCK标志。这个标志定义在&lt;linux/fcntl.h&gt;中,在最近的内核中,这<BR>个头文件由&lt;linux/fs.h&gt;自动包含了。如果你在内核1.2中编译你的模块,你需要手动包<BR>含fcntl.h。<BR>&nbsp;<BR>这个标志的名字取自“打开-非阻塞”,因为这个标志可以在打开时指定(而且,最初只<BR>能在打开时指定)。由于进程在等待数据时的正常行为就是睡眠,这个标志默认情况下<BR>是复位的。在阻塞型操作的情况下,应该实现下列操作:<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果进程调用read,但(尚)没有数据,进程必须阻塞。当数据到达时,进程<BR>被唤醒,并将数据返回给调用者,即便少于方法的count参数中所请求的数据量,也是如<BR>此。<BR>&nbsp;<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果进程调用了write,缓冲区又没有空间,进程也必须阻塞,而且它必须使<BR>用与用来实现读的等待队列不同的等待队列。当数据写进设备后,输出缓冲区中空出部<BR>分空间,唤醒进程,write调用成功完成,如果缓冲区中没有请求中count个字节,则进<BR>程可能只是完成了部分写。<BR>&nbsp;<BR>前面的列表的两个语句都假设,有一个输入和输出缓冲区,而且每个设备驱动程序都有<BR>一个。输入缓冲区需要用来在数据达到而又没有人读时避免丢失数据,输出缓冲区用来<BR>尽可能增强计算机的性能,尽管这样做不是严格必须的。由于如果系统调用不接收数据<BR>的话,数据仍然保存在用户空间的缓冲区中,write中可以丢失数据。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>在驱动程序中实现输出缓冲区可以获得一定的性能收益,这主要是通过较少了用户级/内<BR>核级转换和上下文切换的数目达到的。如果没有输出缓冲区(假设是一个慢设备),每<BR>次系统调用只接收一个或很少几个字节,并且当进程在write中睡眠时,另一进程就会运<BR>行(有一次上下文切换)。当第一个进程被唤醒后,它恢复运行(又一次上下文切换)<BR>,write返回(内核/用户转换),进程还要继续调用系统调用写更多的数据(内核/用户<BR>转换);然后调用再次被阻塞,再次进行整个循环。如果输出缓冲区足够大,write首次<BR>操作时就成功了;数据在中断时被推送给设备,而不必将控制返回用户空间。适合于设<BR>备的输出缓冲区的尺寸显然是和设备相关的。<BR>&nbsp;<BR>我们没有在scull中使用输入缓冲区,这是因为当调用read时,数据已经就绪了。类似地<BR>,也没有使用输出缓冲区,数据简单地复制到设备对应的内存区中。我们将在第9章“中<BR>断处理”的“中断驱动的I/O”一节中介绍缓冲区的使用。<BR>&nbsp;<BR>如果设置了O_NONBLOCK标志,read和write的行为是不同的。此时,如果进程在没有数据<BR>就绪时调用了read,或者在缓冲区没有空间时调用了write,系统简单地返回-EAGAIN。<BR>&nbsp;<BR>如你所料,非阻塞型操作立即返回,允许应用查询数据。当使用stdio函数处理非阻塞型<BR>文件时,由于你很容易误将非阻塞返回认做是EOF,应用程序应该非常小心。你必须始终<BR>检查errno。<BR>&nbsp;<BR>你也许可以从它的名字猜到,O_NONBLOCK在open方法也可有作用。当open调用可能会阻<BR>塞很长时间时,就需要O_NONBLOCK了;例如,当打开一个FIFO文件而又(尚)无写者时<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>塞很长时间时,就需要O_NONBLOCK了;例如,当打开一个FIFO文件而又(尚)无写者时<BR>,或是访问一个被锁住的磁盘文件时。通常,打开设备成功或失败,无需等待外部事件<BR>。但是,有时打开设备需要需要很长时间的初始化,你可以选择打开O_NONBLOCK,如果<BR>设置了标志,在设备开始初始化后,会立即返回一个-EAGAIN(再试一次)。你还可以为<BR>支持访问策略选择实现阻塞型open,方式与文件锁类似。我们稍后将在“替代EBUSY的阻<BR>塞型打开”一节中看到这样一种实现。<BR>&nbsp;<BR>只有read,write和open文件操作受非阻塞标志的影响。<BR>&nbsp;<BR>样例实现:scullpipe<BR>/dev/scullpipe设备(默认有4个设备)是scull模块的一部分,用来展示如何实现阻塞<BR>型I/O。<BR>&nbsp;<BR>在驱动程序内部,阻塞在read调用的进程在数据达到时被唤醒;通常会发出一个中断来<BR>通知这样一种事件,驱动程序在处理中断时唤醒进程。由于你应该无需任何特殊硬件―<BR>―没有任何中断处理函数,就可以在任何计算机上运行scull,scull的目标与传统驱动<BR>程序完全不同。我选择的方法是,利用另一个进程产生数据,唤醒读进程;类似地,用<BR>读进程唤醒写者。这种实现非常类似与一个FIFO(或“命名管道”)文件系统节点的实<BR>现,设备名就出自此。<BR>&nbsp;<BR>设备驱动程序使用一个包含两个等待队列和一个缓冲区的设备结构。缓冲区的大小在通<BR>常情况下是可以配置的(编译时,加载时和运行时)。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>(代码)<BR>&nbsp;<BR>read实现管理阻塞型和非阻塞型数据,如下所示:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>如你所见,我在代码中留下了PDEBUG语句。当你编译驱动程序时,你可以打开消息,这<BR>样就可以更容易地看到不同进程间的交互了。<BR>&nbsp;<BR>跟在interruptible_sleep_on后的if语句处理信号处理。这条语句保证对信号恰当和预<BR>定的处理过程,它会让内核完成系统调用重启或返回-EINTR(内核在内部处理-ERESTART<BR>SYS,最终返回到用户空间的是-EINTR)。我不想让内核对阻塞信号完成这样的处理,主<BR>要时我想忽略这些信号。否则,我们可以返回-ERESTARTSYS错误给内核,让它完成它的<BR>处理工作。我们将在所有的read和write实现中使用一样的语句进行信号处理。<BR>&nbsp;<BR>write的实现与read非常相似。它唯一的“特殊”功能时,它从不完全填充缓冲区,总时<BR>留下至少一个字节的空洞。因此,当缓冲区空的时候,wp和rp时相等的;当存在数据时<BR>,它们是不等的。<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>正如我所构想的,设备没有实现阻塞型open,这要比实际的FIFO要简单得多。如果你想<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>正如我所构想的,设备没有实现阻塞型open,这要比实际的FIFO要简单得多。如果你想<BR>要看看实际的代码,你可以在内核源码的fs/pipe.c中找到那些代码。<BR>&nbsp;<BR>要测试scullpipe设备的阻塞型操作,你可以在其上运行一些应用,象往常一样,可以使<BR>用输入/输出重定义等方法。由于普通程序不执行非阻塞型操作,测试非阻塞活动要麻烦<BR>些。misc-progs源码目录中包含了一个很简单的程序,称为nbtest,用它来测试非阻塞<BR>型操作,该程序罗列如下。它所做的就是使用非阻塞型I/O复制它的输入和输出,并在期<BR>间稍做延迟。延迟时间可以通过命令行传递,默认情况下时1秒钟。<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>Select<BR>在使用非阻塞型I/O时,应用程序经常要利用select系统调用,当涉及设备文件时,它依<BR>赖于一个设备方法。这个系统调用还用来实现不同源输入的多路复用。在下面的讨论中<BR>,我假设你知道在用户空间中select的语义的用法。注意,内核2.1.23引入了poll系统<BR>调用,因此为了支持这两个系统调用,它改变驱动程序方法的工作方式。<BR>&nbsp;<BR>为了保存所有正在等待文件(或设备)的信息,Linux&nbsp;2.0的select系统调用的实现使用<BR>了select_table结构。再次提醒你,你无需了解它的内部结构(但不管怎样,我们一会<BR>会稍做介绍),而且只允许调用操作该结构的函数。<BR>&nbsp;<BR>当select方法发现无需阻塞时,它返回1;当进程应该等待,它应该“几乎”进入睡眠状<BR>态。在这种情况下,要在select_table结构中加入等待队列,并且返回0。<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>态。在这种情况下,要在select_table结构中加入等待队列,并且返回0。<BR>&nbsp;<BR>仅当选择的文件中没有一个可以接收或返回数据时,进程才真正进入睡眠状态。这一过<BR>程发生在fs/select.c的sys_select中。<BR>&nbsp;<BR>写select操作的代码要比介绍它要容易得多,现在就可以scull中时如何实现的:<BR>&nbsp;<BR>(代码)<BR>&nbsp;<BR>这里没有代码处理“第3种形式的选择”,选择异常。这种形式的选择时通过mode&nbsp;==<BR>SEL_EX标别的,但大多数时候你都将其编写为默认情况,在其他选择均失败时执行。异<BR>常事件的含义与设备有关,所以你可以选择是否在你自己的驱动程序中实现它们。这种<BR>功能将只会为专为你的驱动程序设计的程序使用,但那并不它的初衷。在这方面,它与<BR>依赖于设备的ioctl调用很相似。在实际使用中,select中异常条件的主要用途是,通知<BR>网络连接上带外(加急)数据的达到,但它还用在终端层以及管道/FIFO实现中(你可以<BR>查看fs/pipe.c中的SEL_EX)。不过要注意,其他Unix系统的管道和FIFO没有实现异常条<BR>件。<BR>&nbsp;<BR>这里给出的select代码缺少对文件尾的支持。当read调用达到文件尾时,它应该返回0,<BR>select必须通过通告设备可读来支持这种行为,这样应用程序就不会永远等待调用read<BR>了。例如,在实际的FIFO中,当所有的写者都关闭了文件时,读者会看到文件尾,而在s<BR>cullpipe中,读者永远也看不到文件尾。设计这种不同行为的原因时,一般将FIFO当做<BR>两个进程间的通信通道,而scullpipe是一个只要至少有一个读者,所有人就都可以输入<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P>两个进程间的通信通道,而scullpipe是一个只要至少有一个读者,所有人就都可以输入<BR>数据的垃圾筒。此外,也没有必要重新实现内核中已经有了的设备。<BR>&nbsp;<BR>象FIFO那样实现文件尾意味着要在read和读select中检查dev-&gt;nwriters,并做相应处理<BR>。不过很遗憾,如果读者在写者前打开设备,它马上就看到文件尾了,没有机会等待数<BR>据到达。最好的解决这个问题的方法是,实现阻塞型open,这个任务做为练习留给读者<BR>。<BR>&nbsp;<BR>与read和write的交互<BR>select调用的目的是事先判断是否有I/O操作会阻塞。从这个方面说,它时对read和writ<BR>e的补充。由于select可以让驱动程序同时等待多个数据流(但这与这里的情况无关),<BR>select在这方面也时很有用途的。<BR>&nbsp;<BR>为了让应用正确工作,正确实现这3个调用时非常重要的。尽管下面的规则已经多多少少<BR>谈过了一些,我还要在这里再总结一下。<BR>&nbsp;<BR>从设备读取数据<BR>如果在输入缓冲区中有数据,即便比所请求的数据少,而且驱动程序可以保证剩下的数<BR>据会马上达到,read调用应该不经过任何可以察觉的延迟立即返回。如果你至少可以返<BR>回1个字节,而且很方便的话,你总可以返回比请求少的数据(我们在scull就是这样做<BR>的)。当前内核中总线鼠标的实现在这方面就时错的,某些程序(如dd)无法正确读取<BR>这些设备。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>如果输入缓冲区中没有数据,在至少有一个字节可读前read必须阻塞,除非设置了O_NON<BR>BLOCK。非阻塞型read立即返回-EAGAIN(尽管在这种情况下某些旧的System&nbsp;V会返回0)<BR>。在至少有一个字节可读前,select必须报告设备不可读。只要有数据可读,我们就使<BR>用上一条规则。<BR>&nbsp;<BR>如果我们到了文件尾,,无论是否有O_NONBLOCK,read都应该立即返回0。select应该报<BR>告说文件可读。<BR>&nbsp;<BR>向设备写数据<BR>如果输出缓冲区有空间,write应该不做任何延迟返回。它可以接收少于请求数目的数据<BR>,但是它须接收至少一个字节。在这种情况下,select应该报告设备可写。<BR>&nbsp;<BR>如果输出缓冲区是满的,在空间释放前write一直阻塞,除非设置了O_NONBLOCK标志。非<BR>阻塞型write立即返回,返回值为-EAGAIN(或者在某些条件为0,如前面旧版本的System<BR>&nbsp;V所说)。select应该报告文件不可写。但另一方面,如果设备不能接收任何数据,无<BR>论是否设置了O_NONBLOCK,write都返回-ENOSPC(“设备无可用空间”)。<BR>&nbsp;<BR>如果使用设备的程序需要确保等候在输出队列中的数据真的完成了传送,驱动程序必须<BR>提供一个fsync方法。例如,可移动设备使用提供fsync入口点。千万不要在调用返回前<BR>让write调用等待数据传送结束。这是因为,需要应用程序都可用select检查设备是否时<BR>可以写的。如果设备报告可以写,write调用应该保持一致,不能阻塞。<BR>&nbsp;<BR></P></FONT><FONT 
      color=#ffffff size=3>
      <P><BR>刷新待处理输出<BR>我们已经看到write方法为什么不能满足所有数据输出的需求。通过同名系统调用调用的<BR>fsync函数弥补了这一空缺。<BR>&nbsp;<BR>如果某些应用需要确保数据传送到设备上,设备就必须实现fsync方法。无论是否设置了<BR>O_NONBLOCK标志,fsync调用应该仅在设备已经完全刷新数据后才能返回,甚至花些时间<BR>也要如此。<BR>&nbsp;<BR>fsync方法没有什么不寻常的功能。调用不是时间关键的,所以每个设备驱动程序都可以<BR>按照作者的风<BR>&nbsp;<BR>--<BR>&nbsp;<BR><FONT 
      color=#00ff00>※&nbsp;来源:.华南网木棉站&nbsp;bbs.gznet.edu.cn.[FROM:&nbsp;202.38.196.234]</FONT><BR>--<BR><FONT 
      color=#00ffff>※&nbsp;转寄:.华南网木棉站&nbsp;bbs.gznet.edu.cn.[FROM:&nbsp;211.80.41.106]</FONT><BR>--<BR><FONT 
      color=#0000ff>※&nbsp;转寄:.华南网木棉站&nbsp;bbs.gznet.edu.cn.[FROM:&nbsp;211.80.41.106]</FONT><BR>--<BR><FONT 
      color=#ffff00>※&nbsp;转载:.南京大学小百合站&nbsp;bbs.nju.edu.cn.[FROM:&nbsp;211.80.41.106]</FONT><BR>&nbsp;<BR>--<BR><FONT 
      color=#ff0000>※&nbsp;转载:·饮水思源&nbsp;bbs.sjtu.edu.cn·[FROM:&nbsp;211.80.41.106]</FONT><BR></P></FONT>
      <P align=center><A 
      href="http://211.71.69.201/joyfire/lsdp/index.htm"><FONT color=#ffffff 
      size=2>目录页</FONT></A> | <A 
      href="http://211.71.69.201/joyfire/lsdp/6.htm"><FONT color=#ffffff 
      size=2>上一页</FONT></A> | <A 
      href="http://211.71.69.201/joyfire/lsdp/8.htm"><FONT color=#ffffff 
      size=2>下一页</FONT></A></P></SPAN></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>
  <TBODY>
  <TR>
    <TD colSpan=3 height=2>
      <TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#666666 
        border=0><TBODY>
        <TR>
          <TD width="38%" height=4>

⌨️ 快捷键说明

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