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

Linux下如何检查串口是否已打开?含Posix/C函数实现方法

在Linux中检查串口是否已打开(含Posix/C实现)

刚好之前处理过类似的需求,给你整理两种靠谱的方法,不管是手里已经有文件描述符,还是只知道串口设备文件名,都能搞定:


一、通过文件描述符判断串口是否打开

如果你已经拿到了串口的文件描述符(比如调用open()后得到的fd),核心要做两件事:

  1. 先判断fd是否有效:Posix环境里,有效的打开文件描述符值大于等于0,如果是-1,那肯定没打开。
  2. 验证这个fd确实对应串口:可以尝试获取串口的终端属性(用tcgetattr()),能成功获取就说明这是个已打开的串口fd。

给你补全并优化下你提供的示例代码:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <termios.h>

int open_port() {
    int fd;
    // 以读写、非阻塞方式打开串口,加上O_EXCL尝试排他打开(已被占用会直接返回EBUSY)
    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL);
    if (fd == -1) {
        if (errno == EBUSY) {
            printf("串口/dev/ttyUSB0已经被其他进程占用\n");
        } else {
            perror("打开串口失败");
        }
        return -1;
    }
    printf("串口成功打开,文件描述符:%d\n", fd);
    return fd;
}

// 检查给定的fd是否是已打开的串口
int is_serial_fd_valid(int fd) {
    if (fd < 0) {
        return 0; // 无效fd,直接返回未打开
    }

    struct termios term_attr;
    // 尝试获取串口属性,成功则说明是已打开的串口
    if (tcgetattr(fd, &term_attr) == 0) {
        return 1;
    } else {
        // 可能不是串口,或者fd已被关闭
        return 0;
    }
}

二、通过串口文件名判断是否已打开

如果手里只有串口设备路径(比如/dev/ttyUSB0),有两种常用实现方式:

方法1:尝试以排他方式打开设备

这个方法简单高效,核心是给open()加O_EXCL标志,如果设备已经被其他进程打开,open()会返回-1且errno设为EBUSY。

代码示例:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

int is_serial_device_in_use(const char* dev_path) {
    // 以读写、非阻塞、排他方式打开串口
    int fd = open(dev_path, O_RDWR | O_NOCTTY | O_NONBLOCK | O_EXCL);
    if (fd == -1) {
        if (errno == EBUSY) {
            return 1; // 设备已被其他进程打开
        } else {
            perror("检查串口状态时出错");
            return -1; // 其他错误(比如设备不存在、权限不足)
        }
    } else {
        close(fd); // 没被占用,打开后立刻关闭
        return 0;
    }
}

方法2:遍历/proc目录查找已打开的设备

Linux的/proc文件系统会暴露所有进程的文件描述符信息,我们可以遍历每个进程的/proc/<pid>/fd目录,通过符号链接判断是否有进程打开了目标串口。这种方法不需要打开设备,但效率稍低,且依赖/proc文件系统挂载。

代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>

int check_serial_opened_by_proc(const char* dev_path) {
    DIR* proc_dir = opendir("/proc");
    if (!proc_dir) {
        perror("无法访问/proc目录");
        return -1;
    }

    struct dirent* proc_entry;
    char fd_link_path[256], target_path[256];
    ssize_t link_len;

    while ((proc_entry = readdir(proc_dir)) != NULL) {
        // 只处理数字命名的进程目录(pid是数字)
        if (!isdigit(proc_entry->d_name[0])) {
            continue;
        }

        // 拼接该进程的fd目录路径
        snprintf(fd_link_path, sizeof(fd_link_path), "/proc/%s/fd", proc_entry->d_name);
        DIR* fd_dir = opendir(fd_link_path);
        if (!fd_dir) {
            continue; // 无权限访问该进程的fd,跳过
        }

        struct dirent* fd_entry;
        while ((fd_entry = readdir(fd_dir)) != NULL) {
            if (fd_entry->d_name[0] == '.') {
                continue; // 跳过.和..目录
            }

            // 拼接fd的符号链接路径
            snprintf(fd_link_path, sizeof(fd_link_path), "/proc/%s/fd/%s", proc_entry->d_name, fd_entry->d_name);
            // 读取符号链接指向的目标路径
            link_len = readlink(fd_link_path, target_path, sizeof(target_path)-1);
            if (link_len == -1) {
                continue;
            }
            target_path[link_len] = '\0';

            // 对比是否是目标串口
            if (strcmp(target_path, dev_path) == 0) {
                printf("串口%s被进程PID=%s打开\n", dev_path, proc_entry->d_name);
                closedir(fd_dir);
                closedir(proc_dir);
                return 1;
            }
        }
        closedir(fd_dir);
    }

    closedir(proc_dir);
    printf("串口%s未被任何进程打开\n", dev_path);
    return 0;
}

两种方法的优缺点对比

方法优点缺点
排他打开法实现简单、执行高效会短暂打开设备,若设备设置了独占模式可能影响其他进程
/proc遍历法无需打开设备,不影响目标串口效率较低,依赖/proc文件系统,需要足够权限访问进程fd目录

内容的提问来源于stack exchange,提问作者Xofo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:24:32