Linux阻塞与非阻塞串行读取

时间:2014-09-23 13:18:27

标签: linux serial-port blocking nonblocking

我有this code来读取Linux中的Serial,但我不知道在读取串口时阻塞和非阻塞之间的区别是什么?在哪种情况下哪个更好?

1 个答案:

答案 0 :(得分:55)

您提到的代码是IMO编码和评论不佳。该代码不符合POSIX实践的可移植性,如Setting Terminal Modes ProperlySerial Programming Guide for POSIX Operating Systems中所述。该代码没有提到它使用非规范(又称原始)模式,并重用"阻塞"和#34;非阻塞"用于描述 VMIN VTIME 属性的术语。

(该代码的作者报告它早于POSIX标准,因此它不符合。这是可以理解的,但是然后发布并提倡使用可能不可移植的旧代码(即按预期运行)另一种情况)值得怀疑。)

"阻塞"的传统定义与#34;非阻塞"读取基于"当"读取调用将返回到您的程序(并使用下一个语句继续执行)以及是否将数据存储在程序的读取缓冲区中。阻塞读取是默认模式,除非通过使用O_NONBLOCK或O_NDELAY选项打开串行端口来请求非阻塞。

规范模式
对于串行端口的阻塞canonical read调用,将始终在提供的缓冲区中返回文本行(也称为记录)(除非发生错误)。只要接收和处理行终止字符,读取调用将阻止(即暂停程序的执行)。

串口的非阻塞规范读取呼叫将始终返回"立即"。读取可能会也可能不会返回任何数据 如果(自上一次读取调用以来)至少接收到一行文本并将其存储在系统缓冲区中,则最旧的行将从系统缓冲区中删除并复制到程序的缓冲区中。返回码将指示数据长度 如果(自上一次读取调用以来)尚未接收和处理行终止字符,则没有(完整的)文本行可用。 read()将返回EAGAIN错误(即-1返回代码, errno 设置为EAGAIN)。然后,您的程序可以执行某些计算,或从其他设备请求I / O,或延迟/休眠。在任意延迟或通过民意调查()选择()通知后,您的程序可以重试读取()

非规范模式
当串行端口配置为非规范模式时, termios c_cc 数组元素 VMIN 应使用VTIME 来控制"阻塞",但这需要在默认阻止模式下打开端口,即不指定O_NONBLOCK打开选项。否则O_NONBLOCK将优先于VMIN和VTIME规范, read()会将 errno 设置为EAGAIN并立即返回-1而不是0当没有可用数据时。 (这是在最近的Linux 3.x内核中观察到的行为;较旧的2.6.x内核可能表现不同。)

termios手册页描述了( c_cc 数组索引) VMIN 作为"非规范读取的最小字符数" 和( c_cc 数组索引) VTIME 作为"超时(以秒为单位)非语言读取"
VMIN 应由您的程序调整,以适应预期的典型消息或数据报长度和/或要检索的数据的最小大小。每读取过程() VTIME 应由您的程序调整,以适应预期的串行数据的典型突发性或到达率和/或等待数据或数据的最长时间。

VMIN VTIME 值相互作用,以确定何时应返回读取的标准;它们的确切含义取决于它们中的哪一个非零。有四种可能的情况 This web page将其解释为:

  • VMIN = 0且VTIME = 0

      

    这是一个完全无阻塞的读取 - 直接从驱动程序的输入队列中立即满足调用。如果数据可用,则将其传送到呼叫者的缓冲区,最多为nbytes并返回。否则立即返回零以指示"没有数据"。我们注意到这是"民意调查"串口,它几乎总是一个坏主意。如果重复完成,它可能会消耗大量的处理器时间并且效率非常低。不要使用这种模式,除非你真的,真的知道你在做什么。

  • VMIN = 0且VTIME> 0

      

    这是一个纯粹的定时阅读。如果数据在输入队列中可用,则它将被传送到呼叫者的缓冲区,最大为nbytes,并立即返回给呼叫者。否则,驱动程序将阻塞,直到数据到达,或者VTIME十分之一从呼叫开始到期。如果计时器在没有数据的情况下到期,则返回零。单个字节足以满足此读取调用,但如果输入队列中有更多可用,则它将返回给调用者。请注意,这是一个整体计时器,而不是一个字符计时器。

  • VMIN> 0和VTIME> 0

      

    当VMIN字符已传输到调用者缓冲区或VTIME十分之一在字符之间到期时,会满足read()。由于此计时器在第一个字符到达之前未启动,因此如果串行线路空闲,则此调用可无限期地阻塞。这是最常见的操作模式,我们认为VTIME是一个字符间超时,而不是整体。此调用永远不应返回零字节读取。

(根据我的经验,VMIN>0 and VTIME>0模式并不像宣传的那样有效。计时器似乎是一个非常短的间隔,远小于1/10秒。我还没有看来它在ARM上运行2.6,在Linux上运行Linux 3.13。在快速波特率(115200),VMIN = 1且VTIME = 1时,read()有时会返回10个或更多字节。但更常见的是它只是部分读取几个字节而不考虑VTIME值。也许这种破坏是首选/期望​​的?在现代快速波特率下,最小0.1秒的消息分离太长(并且不实用)。)

  • VMIN> 0和VTIME = 0
      

    这是一个计数读数,仅当至少VMIN字符已被传输到调用者的缓冲区时才满足 - 没有涉及定时组件。可以从驱动程序的输入队列(调用可以立即返回)或通过等待新数据到达来满足此读取:在这方面,调用可以无限期地阻塞。如果nbytes小于VMIN,我们认为它是未定义的行为。

您提到的代码配置"非阻塞"模式为VMIN = 0且VTIME = 5。这不会导致read()立即返回,就像非阻塞规范读取一样;使用该代码,read()应该总是在返回之前等待至少半秒。 a" nonblocking"的传统定义是你的调用程序在系统调用期间没有被抢占,并且(几乎)立即获得控制权。 要获得(无条件和)立即返回(对于非规范读取),请设置VMIN = 0和VTIME = 0.