Linux系统下如何检测已打开的串口是否仍处于连接状态?
检测USB串口设备断开的最优C语言实现方案
一、通过read()返回值与错误码直接判断
USB串口设备断开时,read()通常不会返回0(除非设备主动发送EOF),而是返回-1,同时errno会被设置为EIO、ENODEV或EPIPE这类特定错误码,直接判断这些错误就能快速检测断开,无需调用外部命令:
#include <unistd.h> #include <errno.h> ssize_t n = read(fd, buffer, BUFFER_SIZE); if (n == -1) { if (errno == EIO || errno == ENODEV) { // 设备已断开 printf("USB串口设备已断开连接\n"); // 此处可处理重连或退出逻辑 } else { // 其他读取错误(如被信号中断) perror("read error"); } } else if (n == 0) { // 少数情况设备主动发送EOF,可视为断开信号 printf("设备发送EOF,可能已断开\n"); }
二、非阻塞模式+多路复用避免挂起
如果代码因read()阻塞无法及时检测断开,可以将串口设为非阻塞模式,配合select()或poll()监听可读事件,同时设置超时定期检查设备状态:
- 先设置非阻塞属性:
#include <fcntl.h> int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- 用
select()监听并定期校验设备:
#include <sys/select.h> #include <sys/stat.h> fd_set read_fds; struct timeval timeout; while (1) { FD_ZERO(&read_fds); FD_SET(fd, &read_fds); // 设置5秒超时,可按需调整 timeout.tv_sec = 5; timeout.tv_usec = 0; int ret = select(fd + 1, &read_fds, NULL, NULL, &timeout); if (ret == -1) { perror("select error"); break; } else if (ret == 0) { // 超时,检查设备文件是否存在 struct stat st; if (stat("/dev/ttyACM0", &st) == -1) { printf("设备已断开(文件不存在)\n"); break; } // 设备仍在线,继续等待 continue; } // 有数据可读,执行读取操作 ssize_t n = read(fd, buffer, BUFFER_SIZE); if (n == -1) { if (errno == EIO || errno == ENODEV) { printf("设备断开\n"); break; } perror("read error"); } else if (n > 0) { // 处理读取到的数据 process_data(buffer, n); } }
三、主动监听UDEV事件(实时检测最优解)
如果需要实时感知设备插拔,推荐使用libudev库直接监听系统设备事件,无需依赖read()的错误反馈,适合需要快速响应的场景:
#include <libudev.h> int monitor_usb_disconnect(const char* dev_path) { struct udev *udev = udev_new(); if (!udev) { perror("udev_new failed"); return -1; } struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev"); udev_monitor_filter_add_match_subsystem_devtype(mon, "tty", NULL); udev_monitor_enable_receiving(mon); int mon_fd = udev_monitor_get_fd(mon); fd_set fds; while (1) { FD_ZERO(&fds); FD_SET(mon_fd, &fds); int ret = select(mon_fd + 1, &fds, NULL, NULL, NULL); if (ret == -1) { perror("select error"); break; } if (FD_ISSET(mon_fd, &fds)) { struct udev_device *dev = udev_monitor_receive_device(mon); if (dev) { const char* action = udev_device_get_action(dev); const char* devnode = udev_device_get_devnode(dev); // 判断是否为目标设备的移除事件 if (strcmp(action, "remove") == 0 && strcmp(devnode, dev_path) == 0) { printf("目标USB串口设备已断开\n"); udev_device_unref(dev); break; } udev_device_unref(dev); } } } udev_monitor_unref(mon); udev_unref(udev); return 0; }
使用时可将此逻辑放到单独线程中,不影响主程序的读取流程。
内容的提问来源于stack exchange,提问作者lotek
相关产品推荐
相关产品推荐

