You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()监听可读事件,同时设置超时定期检查设备状态:

  1. 先设置非阻塞属性:
#include <fcntl.h>

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
  1. 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 04:00:05