Linux阻塞与非阻塞串行读取

say*_*aee 25 linux serial-port nonblocking blocking termios

我有这个代码用于从Linux中的Serial读取,但我不知道在读取串行端口时阻塞和非阻塞之间的区别是什么?在哪种情况下哪个更好?

saw*_*ust 62

您提到的代码是IMO编码和评论不佳.该代码不符合POSIX实现的可移植性,如正确设置终端模式POSIX操作系统的串行编程指南中所述.该代码未提及它使用非规范(又称原始)模式,并重用"阻塞"和"非阻塞"术语来描述VMINVTIME属性.

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

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

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

非规范模式
当串行端口配置为非规范模式时,应使用termios c_cc数组元素VMINVTIME来控制"阻塞",但这需要在默认阻塞模式下打开端口,即不要指定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,以适应预期的串行数据的典型突发性或到达率和/或等待数据或数据的最长时间.

VMINVTIME值进行交互,以确定何时读应该返回的标准; 它们的确切含义取决于它们中的哪一个非零.有四种可能的情况.
该网页将其解释为:

  • 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 3.13 x86.在快速波特率(115200),VMIN = 1且VTIME = 1时,read()有时返回10个或更多字节.但更常见的是,无论VTIME值如何,它只是部分读取几个字节.也许这个优先/期望破碎?在现代快速波特率下,最小0.1秒的消息分离太长(并且不实用).)

  • VMIN> 0且VTIME = 0

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

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

  • 该代码将POSIX预定了几年,因此它不匹配也就不足为奇了. (12认同)
  • 很好的解释,但是哇。老实说,与 Windows 所做的相比,这是一份相当混乱的合同(http://msdn.microsoft.com/en-us/library/windows/desktop/aa363190%28v=vs.85%29.aspx) (4认同)
  • @sawdust:该代码基于大约1985年的Unix代码.我提供的代码是1992年的最后一次实质性更新,经过各种调整,以支持Solaris,Linux和Unix的一些衍生产品. (4认同)
  • @CMCDragonkai -- 以 *"A nonblocking canonical read..."* 开头的长段落回答了您的问题。 (2认同)