关于ZAKER 融媒体解决方案 合作 加入

Linux 阻塞与非阻塞串行读取

CocoaChina 09-16

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

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

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

规范模式

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

串行端口的非阻塞规范读取调用将始终 " 立即 " 返回 . 读取可能会也可能不会返回任何数据 .

如果 ( 自上次读取调用以来 ) 至少接收到一行文本并将其存储在系统缓冲区中 , 则最旧的行将从系统缓冲区中删除并复制到程序的缓冲区中 . 返回码将指示数据长度 .

如果 ( 自上一次读取调用以来 ) 尚未接收和处理行终止字符 , 则没有 ( 完整的 ) 文本行可用 . read ( ) 将返回 EAGAIN 错误 ( 即 -1 返回码和 errno 设置为 EAGAIN ) . 然后 , 您的程序可以执行某些计算 , 或从其他设备请求 I / O, 或延迟 / 休眠 . 在任意延迟之后或通过 poll ( ) 或 select ( ) 通知 , 您的程序可以重试 read ( ) .

使用阻塞规范模式进行读取的示例程序包含在this answer中 .

非规范模式

当串行端口配置为非规范模式时 , 应使用 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

This is a completely non-blocking read – the call is satisfied immediately directly from the driver ’ s input queue. If data are available, it ’ s transferred to the caller ’ s buffer up to nbytes and returned. Otherwise zero is immediately returned to indicate "no data". We ’ ll note that this is "polling" of the serial port, and it ’ s almost always a bad idea. If done repeatedly, it can consume enormous amounts of processor time and is highly inefficient. Don ’ t use this mode unless you really, really know what you ’ re doing.

> VMIN = 0 且 VTIME> 0

This is a pure timed read. If data are available in the input queue, it ’ s transferred to the caller ’ s buffer up to a maximum of nbytes, and returned immediately to the caller. Otherwise the driver blocks until data arrives, or when VTIME tenths expire from the start of the call. If the timer expires without data, zero is returned. A single byte is sufficient to satisfy this read call, but if more is available in the input queue, it ’ s returned to the caller. Note that this is an overall timer, not an intercharacter one.

> VMIN> 0 和 VTIME> 0

A read ( ) is satisfied when either VMIN characters have been transferred to the caller ’ s buffer, or when VTIME tenths expire between characters. Since this timer is not started until the first character arrives, this call can block indefinitely if the serial line is idle. This is the most common mode of operation, and we consider VTIME to be an intercharacter timeout, not an overall one. This call should never return zero bytes read.

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

> VMIN> 0 和 VTIME = 0

This is a counted read that is satisfied only when at least VMIN characters have been transferred to the caller ’ s buffer – there is no timing component involved. This read can be satisfied from the driver ’ s input queue ( where the call could return immediately ) , or by waiting for new data to arrive: in this respect the call could block indefinitely. We believe that it ’ s undefined behavior if nbytes is less then VMIN.

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

要获得 ( 无条件和 ) 立即返回 ( 对于非规范读取 ) , 请设置 VMIN = 0 和 VTIME = 0.

以上内容由"CocoaChina"上传发布 查看原文

觉得文章不错,微信扫描分享好友

扫码分享