QSocketNotifier监听FIFO时持续触发信号问题排查
解决QSocketNotifier监听FIFO时持续触发信号导致CPU占用100%的问题
咱们来拆解下你遇到的问题:当FIFO的写端关闭后,代码会陷入无限触发activated信号的循环,直接把CPU拉满。核心原因是非阻塞模式下,FIFO所有写端关闭时,read()会返回0(表示EOF),而QSocketNotifier会把这种情况判定为文件描述符“可读”,所以会持续发送信号。你的代码只判断了rl <=0就break,但没区分是EOF还是暂时无数据,导致每次信号触发都跑一遍循环,然后Notifier立刻又触发,形成死循环。
具体修复方案
你需要在读取逻辑里区分read()返回值的不同情况,针对性处理:
- 当
read()返回0:说明所有写端已关闭,必须禁用QSocketNotifier,阻止它继续触发信号;如果之后还需要接收新写入,可能需要重新打开FIFO并创建新的Notifier。 - 当
read()返回-1:检查错误码,若是EAGAIN或EWOULDBLOCK,说明当前无数据可读,正常break即可;其他错误则需关闭FIFO并清理Notifier资源。
修改后的代码示例
// create the FIFO if(!mkfifo(SERIAL_FIFO, 0600)) { // nonblocking open (even open itself would block until a first write) in_fifo = ::open(SERIAL_FIFO, O_RDONLY | O_NONBLOCK); if(in_fifo >= 0) { // create notifier in_fifo_notifier = new QSocketNotifier(in_fifo, QSocketNotifier::Read, this); connect(&*in_fifo_notifier, &QSocketNotifier::activated, this, [this](QSocketDescriptor /*socket*/, QSocketNotifier::Type /*type*/){ // copy all the available data char buf[4096]; for(;;) { ssize_t rl = ::read(in_fifo, buf, sizeof(buf)); if(rl > 0) { ::write(out_fd, buf, rl); } else if(rl == 0) { // EOF: all writers closed, disable notifier to stop spamming in_fifo_notifier->setEnabled(false); // 如果之后需要重新监听,可以在这里记录状态,后续重新打开FIFO break; } else { // read error, check if it's just no data available if(errno != EAGAIN && errno != EWOULDBLOCK) { // real error, clean up in_fifo_notifier->setEnabled(false); ::close(in_fifo); in_fifo = -1; delete in_fifo_notifier; in_fifo_notifier = nullptr; } break; } } }); } }
额外注意事项
- 如果需要支持写端关闭后重新打开的场景,不能一直持有当前的FIFO文件描述符(它会一直处于可读返回EOF的状态),需在收到EOF后关闭描述符,再通过定时检查或文件系统监控事件重新打开FIFO并创建新的QSocketNotifier。
- 对象销毁时,记得检查并关闭
in_fifo、删除in_fifo_notifier,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Matteo Italia
相关产品推荐
相关产品推荐

