Linux阻塞与非阻塞串行读取

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

你提到的代码是国际海事组织编码差,评论。 该代码不符合POSIX实用程序的可移植性,如正确设置终端模式和POSIX操作系统的串行编程指南中所述 。 该代码没有提到它使用非规范(又名原始)模式,并重复使用“阻塞”和“非阻塞”术语来描述VMINVTIME属性。

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

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

串行端口的非阻塞规范读取调用将始终返回“立即”。 读取可能会或可能不会返回任何数据。
如果(自上次读取调用以来)至少有一行文本已被接收并存储在系统缓冲区中,则最早的行将从系统缓冲区中移除并复制到程序的缓冲区中。 返回码将指示数据长度。
如果(自上次读取调用以来)行终止字符没有被接收和处理,则没有(完整)行文本可用。 read()将返回一个EAGAIN错误(即-1返回码和errno设置为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。