Linux非规范模式串口通信如何忽略收到的POLLHUP事件?
Linux串口非规范模式随机POLLHUP问题解决方法
问题根因
- 未开启
CLOCAL标志:termios默认会检测调制解调器载波信号(DCD),直连UART设备时无稳定载波信号,会随机触发挂断检测 - 禁用了
IGNBRK:收到串口BREAK条件(对端发送超过1字节长度的低电平)时,内核默认触发挂断事件上报 - open返回值判断错误:原代码判断
fd == 0作为打开失败条件,而0是标准输入的合法fd,会导致意外初始化失败 - 错误的错误检测逻辑:串口不是套接字设备,调用
getsockopt读取SO_ERROR返回的结果无意义
修复方案
1. 修正初始化配置
修改setup()方法中的termios配置:
bool UartSocket::setup() { int fd = ::open("/dev/serial0", O_RDWR | O_NOCTTY | O_NONBLOCK); // 修正open返回值判断:open失败返回-1,不是0 if (fd < 0) { return false; } struct termios portSettings; ::memset(&portSettings, 0, sizeof(portSettings)); if (m_rSyscalls.tcgetattr(fd, &portSettings) != 0) { m_logger.error("tcgetattr() failed, errno = %d.", errno); return false; } m_rSyscalls.cfsetispeed(&portSettings, 115200); m_rSyscalls.cfsetospeed(&portSettings, 115200); cfmakeraw(&portSettings); // 核心修改1:忽略BREAK条件,避免触发挂断事件 portSettings.c_iflag |= IGNBRK; // 核心修改2:忽略载波检测,启用接收器,关闭挂断自动复位 portSettings.c_cflag |= CLOCAL | CREAD; portSettings.c_cflag &= ~HUPCL; // 非规范模式参数配置:至少1字节返回,无超时 portSettings.c_cc[VMIN] = 1; portSettings.c_cc[VTIME] = 0; if (m_rSyscalls.tcsetattr(fd, TCSANOW, &portSettings) != 0) { m_logger.error("tcsetattr() failed, errno = %d.", errno); return false; } m_pollfd.fd = fd; m_pollfd.events = POLLIN; return true; }
2. 优化POLLHUP处理逻辑
串口的POLLHUP仅表示输入侧的临时挂断事件,不会导致fd失效,无需重新打开设备,修改recv()中的处理逻辑:
// 缓冲区清空后再处理挂起事件 if ((m_pollfd.revents & POLLHUP) && (readResult == 0)) { m_logger.warn("Hangup event on uart %s, ignored.", m_rConfig.getPath().c_str()); // 清除事件标记即可,无需reopen m_pollfd.revents &= ~POLLHUP; }
验证说明
修改后内核会自动忽略BREAK条件和载波电平变化,不会再随机触发POLLHUP事件,即便出现偶发事件也无需重启设备,所有原始字节都会正常上报到应用层,不会被内核解析为控制字符。
内容的提问来源于stack exchange,提问作者Fadeway
相关产品推荐
相关产品推荐

