Linux串口:带超时的阻塞读取

5
我学习了许多有用的线程和教程,但是我仍然遇到了一些问题,这应该是非常简单的事情。以下是我查阅过的一些线程供参考: 如何在read函数调用中实现超时? 如何在C中打开、读取和写入串口? 无论如何,我有一个小问题。如果我接收到数据,我的代码就可以正常工作。如果没有,read()函数就会停止运行,而且唯一的退出程序的方法是使用kill -9命令。(注意:我使用信号处理来通知正在读取串口数据的线程终止。这不是罪魁祸首,即使我已经删除了信号处理,read()调用仍会停止运行)。我想做的是有一个阻塞读取并每次读取一个块(因此节省CPU使用),但是如果读取没有接收到数据,我希望它超时。
以下是我应用于端口的设置:
struct termios serial_struct;
serial_struct.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
serial_struct.c_iflag = IGNPAR;
serial_struct.c_oflag = 0;
serial_struct.c_lflag = 0;
serial_struct.c_cc[VTIME] = 1;  // timeout after .1s that isn't working
serial_struct.c_cc[VMIN] = 64;  // want to read a chunk of 64 bytes at a given time

我使用 tcsetattr() 设置这些设置,通过 tcgetattr() 确认端口已接收到这些设置。我认为我的设置可能会发生冲突,因为我的读取似乎是阻塞的,并且等待接收到 64 个字节,但不会对超时做任何处理。我知道可以使用 select() 处理超时,但希望避免多次系统调用。
像往常一样,提前感谢您的帮助。

1
Q: 什么是设备(例如/dev/ttyS0)?此端口上的设备是什么(RS232 COM 端口?还是其他东西?)同时:如果您尚未熟悉它,这是一个很好的链接:http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/。您可能对“异步 I/O”部分感兴趣。 - paulsm4
参见 https://dev59.com/vl8e5IYBdhLWcg3wLYDt 顺便提一下,你的 termios 代码不完整,因此不可靠。请参考 正确设置终端模式POSIX 操作系统串行编程指南 - sawdust
1个回答

6

来自termios第3页的man手册:

MIN > 0; TIME > 0: TIME指定为以1/10秒为单位的计时器时间限制。一旦有输入字节可用,每次接收到进一步的字节后,计时器将重新启动。当请求的字节数或最小字节数中较小值已被读取,或者在间隔达到TIME以1/10秒计算的时间限制后,read(2)将返回。由于计时器仅在初始字节变为可用后才开始,因此至少将读取一个字节。

请注意,计时器只有在接收到至少一个数据字节后才会开始。接收到第一个数据字节后,如果连续接收数据字节之间间隔了TIME乘以1/10秒的时间,则读取超时。


1
哎呀!那termios有没有办法在没有数据传递时设置“真正的”超时时间?那我该怎么解决呢?用select()吗? - It'sPete
1
是的:我引用的链接还讨论了“select()”。问题是:这是什么设备? - paulsm4
1
@It'sPete 我认为几乎每个程序最终都会增长到你要么使用selectpoll来多路复用I/O,要么使用单独的线程处理不同的流。还是早点解决吧。 - Casey
@Casey 我只希望能够从一个设备读取数据,这个设备是可配置的单个串口。一个线程负责从端口读取数据并将其放置在可以被消费者访问的位置。我希望避免多次系统调用,因为读取是在循环中进行的。然而,如果没有其他方法来确定是否正在获取数据,那么看起来我必须这样做... - It'sPete
1
以上答案不完整。除非串行终端处于非阻塞模式并且使用非规范模式,否则VMIN和VTIME将无效。 “那么termios没有办法在没有传递数据的情况下设置“真正的”超时吗?” - 使用VMIN = 0VTIME = <十进制秒超时>并且处于非阻塞,非规范模式)。但不要指望每个系统调用都能收到完整的消息。 - sawdust
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接