在 Linux 系统中,串口(Serial Port,如 /dev/ttyS0、/dev/ttyUSB0 等)默认是共享资源,这意味着多个进程可以同时打开同一个串口设备,这通常会导致数据冲突、丢包或通信失败。
要实现独占串口(Exclusive Access),确保只有一个进程能访问该串口,有以下几种主要方法:
使用 open() 系统调用的 O_EXCL 标志(推荐)
这是最标准、最可靠的方法,通过 open() 函数打开串口时,加上 O_EXCL 标志,内核会确保该设备文件没有被其他进程以独占方式打开。
C 语言示例:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_EXCL);
if (fd == -1) {
if (errno == EBUSY) {
printf("串口已被其他进程独占打开,无法访问,n");
} else {
perror("打开串口失败");
}
} else {
printf("成功独占串口 /dev/ttyS0n");
// 进行串口配置和读写操作...
close(fd);
}
注意:
O_EXCL通常与O_NOCTTY一起使用,避免将串口设为控制终端。
使用 flock() 文件锁(适用于应用层协调)
如果多个进程是你自己编写的,或者你希望在不修改
open() 行为的情况下实现互斥,可以使用 flock() 对串口设备文件进行文件锁。
C 语言示例:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/file.h>
int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
if (fd == -1) {
perror("打开串口失败");
return -1;
}
// 尝试获取独占锁(非阻塞)
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
if (errno == EWOULDBLOCK) {
printf("串口已被其他进程锁定,无法访问,n");
} else {
perror("加锁失败");
}
close(fd);
return -1;
}
printf("成功获取串口独占锁n");
// 进行串口操作...
// 使用完毕后释放锁
flock(fd, LOCK_UN);
close(fd);
优点:即使其他进程没有使用
O_EXCL,只要它们也使用flock加锁,就能实现互斥。
缺点:需要所有访问串口的进程都遵守锁协议,如果有一个进程不使用锁,它会绕过保护。
使用 setserial 或 udev 规则(系统级限制)
某些嵌入式系统或特定硬件平台支持通过内核参数或 setserial 工具设置串口的独占标志。
使用 setserial:
# 将串口设置为独占模式(某些内核版本支持) sudo setserial /dev/ttyS0 exclusive
注意:
setserial的exclusive选项在不同 Linux 内核版本中支持程度不一,现代内核更推荐使用O_EXCL。
使用 udev 规则限制设备权限(安全隔离)
你可以创建一个 udev 规则,将串口的权限设置为仅特定用户或组可访问,从而间接实现“独占”。
示例:创建 udev 规则
# 创建规则文件 /etc/udev/rules.d/99-serial-exclusive.rules
SUBSYSTEM=="tty", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678", MODE="0660", GROUP="serial-users"
然后只允许特定用户组中的进程访问串口。
优点:系统级安全隔离。
缺点:不能防止同一用户下的多个进程同时访问,仍需配合O_EXCL或flock。
常见陷阱与注意事项
-
后台服务占用:
- 检查是否有
getty、systemd-logind、minicom、screen或其他串口服务正在使用串口。 - 禁用不必要的串口服务:
sudo systemctl stop serial-getty@ttyS0.service sudo systemctl disable serial-getty@ttyS0.service
- 检查是否有
-
权限问题:
- 确保运行程序的用戶对串口设备有读写权限(
或/dev/ttyS0
/dev/ttyUSB0)。 - 常用命令:
sudo chmod 666 /dev/ttyS0(临时)或加入dialout组(永久)。
- 确保运行程序的用戶对串口设备有读写权限(
设备文件类型:
- USB 转串口设备(如 CH340、FTDI)的设备名可能是
/dev/ttyUSB0、/dev/ttyACM0等,方法相同。
- USB 转串口设备(如 CH340、FTDI)的设备名可能是
-
内核版本差异:
- 在较新的 Linux 内核中,
O_EXCL对 TTY 设备的行为可能受到tty_port子系统的影响。O_EXCL不生效,建议结合flock()使用。
- 在较新的 Linux 内核中,
总结建议
| 场景 | 推荐方法 |
|---|---|
| 通用 Linux 应用 | O_EXCL + open()(最标准) |
| 多进程协同应用 | flock() 文件锁(需所有进程配合) |
| 嵌入式/旧系统 | setserial exclusive(兼容性差,不推荐) |
| 安全隔离 | udev 规则 + O_EXCL |
最佳实践:在代码中使用 open() 时加上 O_EXCL 标志,并在程序启动时检查是否成功打开,这是最简单、最可靠的独占串口方式。
首发原创文章,作者:世雄 - 原生数据库架构专家,如若转载,请注明出处:https://idctop.com/article/487213.html



