如何在Linux中检查串口是否打开?

7
如何使用Posix / C函数在Linux中检查串口是否已经打开?我想检查串口的状态以确定串口是否已经打开。
我想知道哪些方法适用于:
1. 检查文件描述符,以查看串口是否打开
2. 检查串口文件名,以查看串口是否已打开,例如下面的示例为"/ dev / ttyUSB0"
// This code is for example purposes only

int open_port()
{
  int fd;

  fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd < 0)
  {
    perror("open_port: Unable to open /dev/ttyf1 - ");
  }

  return fd;
} 

我想象中有一种“标准”的方法来做这件事,这就是我试图探究的。


1
这可能是 https://dev59.com/xHXYa4cB1Zd3GeqP2ySV 的重复。 - G. Sliepen
它看起来很相似,但并没有提供解决方案,因为这是如何检查端口是否打开的方式。它只提供了一种独占锁定端口的方法。 - Xofo
1
@Xofo:应用程序应该通过TIOCEXCL ioctl和/或flock()/fcntl()在打开文件描述符时独占锁定端口,以避免其他人能够打开设备并干扰其设置、输入或输出。这是标准解决方案。要检查端口是否正在使用,只需尝试打开它。如果您可以打开它,则没有任何其他应用程序在使用它。所有其他解决方案都是错误的,因为它们包含竞争条件,或者不可靠,或者与设备发现守护程序等存在冲突。 - Nominal Animal
@NominalAnimal - 感谢您的评论。您的评论是一个解决方案。您说您的解决方案是“最佳实践”解决方案。还有其他解决方案吗?我认为它们值得讨论。您提到了TIOCEXCL ioctl和/或flock()/fcntl() - 这两个函数是否都安全? - Xofo
重复的问题:在Linux中锁定串口和其他设备的最佳实践是什么? - Stack Overflow - Craig McQueen
2个回答

8

这个问题有两个方面,需要两种不同的解决方案:

一个使用串口的应用或服务守护程序应该排除其他进程同时使用该端口。
有两种方法可以做到这一点:独占模式和咨询锁。应用程序可以选择执行其中之一,或两者都执行。
1. 打开设备后,使用ioctl(fd, TIOCEXCL)将串口设置为独占模式。在描述符关闭之前,或进程发出ioctl(fd, TIOCNXCL)之前,任何尝试打开该设备的操作都会失败并返回EBUSY错误代码。
2. 打开设备后,使用flock(fd, LOCK_EX | LOCK_NB)尝试对打开的设备进行独占咨询锁定。其他进程仍然可以正常打开设备,甚至读写它,但是尝试对其进行咨询锁定将失败并返回EWOULDBLOCK(如果没有使用LOCK_NB则会阻塞直到解锁或关闭)。
以上两种方法的区别在于后者是协作的,允许其他进程打开设备;而前者则禁止进一步的打开。
使用两种方法的原因是检测是否已经有另一个进程打开了设备,但未将其设置为独占模式,但希望已经设置了咨询锁。在这种情况下,open()和ioctl()都会成功,但flock()会失败。
应用程序或守护程序可以使用lsof(来自lsof软件包)来检查是否有任何进程打开了指定的文件或设备。
其思路是使用root权限运行相当于LANG=C LC_ALL=C lsof -F -p DEVICE的命令。输出将包含零个或多个行。以p开头的行(并紧随PID和换行符\n)指定具有DEVICE打开的进程。(每个这样的行后面跟着一行或多行以f开头,描述该进程中哪个描述符引用该设备。)
如果应用程序或守护程序没有root权限,则需要安装setuid root的帮助程序。它提供一个或多个设备名称。帮助程序使用stat()S_IFCHR(st_mode)验证每个字符设备(以避免帮助程序被用于安全漏洞),然后执行上述命令。在Linux中,这样的帮助程序通常安装在/usr/lib/APPLICATION/中,其中APPLICATION是应用程序或守护程序的名称。
应用程序或守护程序本身可以通过popen("/path/to/helper 2>/dev/null", "r")执行帮助程序,并使用例如fscanf()读取输入。(记得使用pclose()获取状态,并使用例如(WIFEXITED(status) && !WEXITSTATUS(status))验证命令是否成功。
请注意,帮助程序方法允许更容易地将应用程序移植到其他POSIX系统,只需用适合新系统的帮助程序替换即可。即使未安装为setuid root,它也为系统维护人员提供了一个简单的钩子,以修改应用程序或服务的行为,如果有这样的需要。我个人强烈推荐使用帮助程序方法。

1
尽管不完全相关,因为这个答案特别关于 lsof,但是它还是有意义的,因为它提供了如何使用类似于 lsof 的内核调用序列的线索。
包含 LANG=C LC_ALL=C lsof -F -p DEVICE答案对我没用,因为 -p 意味着进程id, 当 lsof 查询一个字符设备(即tty)时,似乎会阻塞很长时间或无限期地挂起。
在阅读了lsof手册之后:
-O
    This option directs lsof to bypass the strategy it uses to avoid being blocked by some kernel operations - i.e., doing them in forked child processes. See the BLOCKS AND TIMEOUTS and AVOIDING KERNEL BLOCKS sections for more information on kernel operations that may block lsof. 
While use of this option will reduce
    lsof startup overhead, it may also cause lsof to hang when the kernel doesn't respond to a function. Use this option cautiously. 

我尝试了-O,它立即生效。

因此,尽管我阅读的手册内容指示相反,但我成功地使用了:

lsof -t -S 2 -O /dev/ttyS0

-t = 输出简略信息(无最小细节,无标题)
-S 2 = 将阻塞内核调用的15秒超时减少为2秒
-O = 要求 lsof 避免可能会阻塞的某些内核调用


感谢您的有益贡献。 - Xofo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接