在某些时候串行 POSIX 写入文件描述符似乎会停止程序
Posted
技术标签:
【中文标题】在某些时候串行 POSIX 写入文件描述符似乎会停止程序【英文标题】:Serial POSIX write to the file descriptor at some point seems to halt the program 【发布时间】:2021-11-02 00:22:59 【问题描述】:我有两个不同的程序,一个通过mq_send
写入消息队列,而另一个通过mq_receive
使用它。
我遇到的一个问题是Consumer
线程中的串行写入似乎在写入串行端口一段时间后导致挂起。
消费者线程在似乎挂起之前能够接收 51 次。当我注释掉串行写入部分时,程序运行良好,没有任何中断。
有趣的是,在修改MAX_MSG_SIZE
值后,我能够收到或多或少的消息。此外,man page 仅谈论接收缓冲区大小大于mq_msgsize
。
这是否意味着串行端口以某种方式阻塞,特别是如果串行端口的接收端没有读取数据?
我没有看到任何函数出现任何错误。
在实际代码中,生产者根据定时器中定义的特定时间间隔写入消息队列,并且存在重叠时间段,其中消息以非常接近的优先级写入。
// common.h
#define MAX_MESSAGES 10
#define MAX_MSG_SIZE 72
#define MSG_BUFFER_SIZE MAX_MSG_SIZE + 10
#define QUEUE_PERMISSIONS 0660
#define COMMON_QUEUE "/MSG_QUEUE"
typedef enum
LEFT,
RIGHT
CmdType;
typedef struct
CmdType cmd;
uint16_t length;
uint8_t payload[64];
Pkt;
消费者
#include "common.h"
int fd; // file descriptor for serial port
static struct mq_attr attr = .mq_flags = 0, .mq_maxmsg = MAX_MESSAGES, .mq_msgsize = MAX_MSG_SIZE, .mq_curmsgs = 0;
void *Consumer(void *args)
uint8_t rcvBuffer[MSG_BUFFER_SIZE];
mqd_t *mqd = (mqd_t *) args;
int ret;
while(1)
ret = mq_receive(*mqd, rcvBuffer, sizeof(rcvBuffer), 0);
if (ret == -1)
perror ("mq_receive failed");
printf ("Received data: %d\n", *rcvBuffer);
// serial write
int sizeWritten = write(fd, rcvBuffer, sizeof(rcvBuffer));
if (sizeWritten < 0)
perror ("Write to serial failed");
int UartInit()
fd = open("/dev/ttyUSB4", O_RDWR);
if (fd == -1)
perror ("Serial port failed to open");
return -1;
if(tcgetattr(fd, &tty) != 0)
perror("Error from tcgetattr");
return -1;
cfsetspeed(tty, B115200); // baud rate to 115200
tty->c_cflag |= CS8;
tty->c_cflag &= ~CSTOPB;
tty->c_cflag |= CRTSCTS; // HW flow control
tty->c_cflag &= ~PARENB;
tty.c_iflag |= ICRNL; // enable translating carriage return to newline on input
tty.c_iflag |= IGNBRK; // ignore break condition
tty.c_iflag &= ~BRKINT;
tty.c_iflag &= ~INPCK; // disable input parity check
tty.c_iflag &= ~PARMRK; // ignore input bytes with parity or framing errors are marked when passed to the program
tty.c_iflag &= ~ISTRIP; // ignore stripping off the 8th bit
tty.c_oflag |= OCRNL; // Map CR to NL on output.
tty.c_oflag |= ONLCR; // Map NL to CR-NL on output.
tty.c_oflag &= ~OPOST; // Disable implementation-defined output processing
tty.c_lflag &= ~ICANON; // Disable canonical mode
tty.c_lflag &= ~ISIG;
tty.c_lflag &= ~IEXTEN; // Disable implementation-defined input processing
tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ECHOCTL|ECHOPRT|ECHOKE);
// settimg serial read blocking behavior
tty.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSANOW, &tty) != 0)
printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
return -1;
return 1
int main()
mqd_t mqd;
int ret = UartInit();
if ((mqd = mq_open(COMMON_QUEUE, O_RDWR | O_CREAT, QUEUE_PERMISSIONS, &attr)) == -1)
perror ("Message queue failed to instantiate");
return -1;
if (pthread_create(&consumerTd, NULL, Consumer, (void *) &mqd) != 0)
return -1;
pthread_join(consumerTd, 0);
return 0;
【问题讨论】:
【参考方案1】:您的代码看起来像是在串行端口上启用流量控制。如果连接的另一端没有表明它已准备好接受数据,则您的串行端口将不会传输任何内容。您将慢慢填满串行端口的传输缓冲区,一旦没有更多空间来缓冲数据,尝试写入更多数据将阻塞并等待空间或返回错误(在您的情况下,是前者)。
我建议检查以确保连接的双方都使用相同的流量控制设置,您的串行电缆实际上具有连接的流量控制引脚(许多便宜的只连接TX/RX/GND 线),并且您的串口支持硬件流控制(USB 转串口适配器通常不支持)。禁用流控制可以避免挂断,但您可能会在对方尚未准备好接收数据时传输数据。
【讨论】:
谢谢。但是如果在接收端禁用了硬件流控制,我不应该收到消息吗?我会在接收端检查,但仍然很好奇接收消息的可能性。 @xyf 如果对方没有开启流量控制,它会随时发送消息。只要你这边准备好接收消息,你就会收到。当您的一方没有准备好(发射机无论如何都会发送而您错过了消息),或者如果您正在等待另一方表明它“准备好接收”(即不支持信号,因此它永远不会被设置)。以上是关于在某些时候串行 POSIX 写入文件描述符似乎会停止程序的主要内容,如果未能解决你的问题,请参考以下文章