Linux下如何检查串口是否已打开?含Posix/C函数实现方法
在Linux中检查串口是否已打开(含Posix/C实现)
刚好之前处理过类似的需求,给你整理两种靠谱的方法,不管是手里已经有文件描述符,还是只知道串口设备文件名,都能搞定:
一、通过文件描述符判断串口是否打开
如果你已经拿到了串口的文件描述符(比如调用open()后得到的fd),核心要做两件事:
- 先判断
fd是否有效:Posix环境里,有效的打开文件描述符值大于等于0,如果是-1,那肯定没打开。 - 验证这个
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
相关产品推荐
相关产品推荐

