Ubuntu虚拟机C++串口读取报Error11错误求助
问题背景
在Ubuntu 24.04.2虚拟机中使用C++与串口交互,已将串口设置为Raw模式,可正常写入数据,但每次读取都会触发Error 11(Resource temporarily unavailable)。已简化程序代码、将当前用户加入dialout组,不清楚是否需要停止相关服务及操作方式。
错误原因
Error 11对应EAGAIN错误,根源是打开串口时使用了O_NDELAY标志,将串口设置为非阻塞模式。当串口接收缓冲区没有数据时,read()会直接返回EAGAIN,这并非设备故障,是非阻塞IO的正常行为。
解决方案
方案1:切换为阻塞模式(推荐)
打开串口时移除O_NDELAY标志,让read()阻塞等待数据:
int fd = open(portName, O_RDWR | O_NOCTTY);
修改后,read()会一直等待直到有数据可读,不会再返回EAGAIN错误。
方案2:保留非阻塞模式并处理错误
如果需要维持非阻塞IO,在读取函数中判断errno是否为EAGAIN,将其视为“当前无数据”而非报错:
void readData(int fd) { char buffer[256]; ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1); if (bytesRead < 0) { if (errno == EAGAIN) { std::cout << "当前无数据可读" << std::endl; return; } printf("Error %i reading from port: %s\n", errno, strerror(errno)); std::cerr << "Error reading data" << std::endl; return; } buffer[bytesRead] = '\0'; std::cout << "Bytes read: " << bytesRead << ", data: " << buffer << std::endl; }
方案3:完善Raw模式配置
串口配置函数缺少c_cc参数设置,Raw模式下需通过VMIN和VTIME控制读取的等待逻辑:
void configurePort(int fd) { struct termios options; tcgetattr(fd, &options); cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); options.c_cflag &= ~PARENB; // 无校验位 options.c_cflag &= ~CSTOPB; // 1位停止位 options.c_cflag &= ~CSIZE; // 重置数据位长度 options.c_cflag |= CS8; // 8位数据位 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 禁用规范模式、回显等 options.c_iflag &= ~(IXON | IXOFF | IXANY); // 禁用软件流控 options.c_oflag &= ~OPOST; // 禁用输出处理(Raw模式必需) // 设置读取等待参数 options.c_cc[VMIN] = 1; // 至少读取1个字节才返回 options.c_cc[VTIME] = 10; // 超时时间1秒(单位:0.1秒) tcsetattr(fd, TCSANOW, &options); // 立即应用配置 }
VMIN > 0时:VTIME是等待第一个字节的超时时间,超时后即使没到VMIN字节数也返回VMIN = 0时:VTIME是每次读取的超时时间,超时后立即返回
关于停止服务的说明
你遇到的问题不需要停止额外服务。若怀疑串口被其他进程占用,可通过以下命令排查:
lsof /dev/ttyS0
若输出结果中有其他进程,可使用kill <PID>终止,但你的情况大概率是非阻塞模式导致的EAGAIN,无需此操作。
原始代码
#include <iostream> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <cstring> void configurePort(int fd) { struct termios options; // Get current port parameters tcgetattr(fd, &options); // Set data rate cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); // Set 8 data bits, no parity, 1 stop bit options.c_cflag &= ~PARENB; // No parity options.c_cflag &= ~CSTOPB; // 1 stop bit options.c_cflag &= ~CSIZE; // Reset data size options.c_cflag |= CS8; // 8 bits of data // Set non-canonical input mode and disable flow control options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_iflag &= ~(IXON | IXOFF | IXANY); // Apply settings tcsetattr(fd, TCSANOW, &options); } void sendData(int fd, const char* message) { ssize_t bytesWritten = write(fd, message, strlen(message)); if (bytesWritten < 0) { std::cerr << "Error sending data" << std::endl; return; } std::cout << "Bytes sent: " << bytesWritten << std::endl; } void readData(int fd) { char buffer[256]; // Read data from the port ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1); if (bytesRead < 0) { printf("Error %i reading from port: %s\n", errno, strerror(errno)); std::cerr << "Error reading data" << std::endl; return; } buffer[bytesRead] = '\0'; // Terminate the string with a null character std::cout << "Bytes read: " << bytesRead << ", data: " << buffer << std::endl; } int main() { const char* portName = "/dev/ttyS0"; // Port name (can be changed to /dev/ttyS1) //Open the port in non-blocking mode int fd = open(portName, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { std::cerr << "Failed to open port " << portName << std::endl; return 1; } // Configure the port configurePort(fd); const char* message = "123456789!sdas!!\n"; // Send data sendData(fd, message); // Read data sleep(1); // Delay to wait for a response readData(fd); // Close the port close(fd); return 0; }
内容的提问来源于stack exchange,提问作者Cmrd_Kirdyk
相关产品推荐
相关产品推荐

