且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-11-28 15:40:46

上述问题有两个方面,实际上需要两种不同的解决方案:

There are two aspects to the stated question, which really require two different solutions:

  1. 使用串行端口的应用程序或服务守护进程应排除其他进程同时使用该端口.

  1. An application or service daemon using a serial port should exclude other processes from using the port concurrently.

有两种方法可以做到这一点:独占模式和咨询锁.应用程序可以选择执行一项,或同时执行两项.

There are two ways of doing this: exclusive mode, and an advisory lock. An application can choose to do one, or both.

  • 打开设备后,使用ioctl(fd, TIOCEXCL) 将串口设置为独占模式.在描述符关闭或进程发出 ioctl(fd, TIOCNXCL) 之前,任何打开设备的尝试都将失败并返回 EBUSY 错误代码.

  • After opening the device, use ioctl(fd, TIOCEXCL) to put the serial port into exclusive mode. Until the descriptor is closed, or the process issues ioctl(fd, TIOCNXCL), any attempt to open the device will fail with EBUSY error code.

打开设备后,使用flock(fd, LOCK_EX | LOCK_NB) 尝试在打开的设备上放置一个独占咨询锁.其他进程仍然可以正常打开设备,甚至可以对其进行读写,但是尝试在其上放置建议性的群锁将失败并显示 EWOULDBLOCK (如果没有 放置,则阻塞直到解锁或关闭>LOCK_NB).
 

After opening the device, use flock(fd, LOCK_EX | LOCK_NB) to try and place an exclusive advisory lock on the open device. Other processes can still open the device normally, and even read and write to it, but trying to place an advisory flock lock on it will fail with EWOULDBLOCK (or block until unlocked or closed if placed without LOCK_NB).
 

上述两种方式的区别在于,后者是协同的,允许其他进程打开设备;而前者不允许进一步打开.

The difference between the above two approaches is that the latter is co-operative, allowing other processes to open the device; while the former disallows further opens.

同时使用两者的原因是为了检测另一个进程是否已经打开了设备而不将其置于独占模式,但希望设置了咨询锁.在这种情况下,open() 和 ioctl() 都成功了,但是 flock() 失败了.

The reason to use both, is to detect if another process has already opened the device without putting it into exclusive mode, but has hopefully set the advisory lock. In that case, the open() and ioctl() both succeed, but the flock() fails.

(我们可以使用下面讨论的这个问题的第二个方面——打开设备后,将其设置为独占模式,甚至获得对它的独占锁——来检测其他进程是否打开了设备但不排他或锁定.就我个人而言,我不会打扰;我确实希望用户只在他们的系统上使用健全的应用程序.如果他们真的这样做,我希望他们有这样做的理由,所以我更愿意允许这种奇怪的情况.标准实用程序绝对不应该发生这种情况.)
 

(We can use the second aspect of this question discussed below — after having opened the device, set it to exclusive mode, and even obtained the exclusive lock on it —, to detect if other processes have the device open but not exclusive or locked. Personally, I wouldn't bother; I do expect users to only use sane applications on their systems. If they really did that, I'd expect them to have a reason for it, so I'd prefer to allow that oddball scenario. It definitely should not occur with standard utilities.)
 

应用程序或守护程序可以使用 lsof(来自 lsof 包)检查是否有任何进程打开了指定的文件或设备.

An application or daemon can use lsof (from the lsof package) to examine if any process has the specified file or device open.

这个想法是使用 root 权限运行 LANG=C LC_ALL=C lsof -F -p DEVICE 的等效项.输出将包含零行或多行.以 p 开头的行(紧跟 PID 和换行符 )指定打开了 DEVICE 的进程.(每一行后面跟着一行或多行以 f 开头的行,描述该进程中哪个描述符引用设备.)

The idea is to run the equivalent of LANG=C LC_ALL=C lsof -F -p DEVICE with root privileges. The output will contain zero or more lines. The lines beginning with p (and immediately followed with the PID and a newline ) specify the processes that have DEVICE open. (Each such line is followed by one or more lines starting with f, describing which descriptor in that process refer to the device.)

如果应用程序或守护程序没有 root 权限,则需要安装 setuid root 的帮助程序.它提供了一个或多个设备名称.帮助程序使用 stat()S_IFCHR(st_mode) 验证每个是字符设备(以避免帮助程序被用于安全漏洞);如果是,则执行上述命令.在 Linux 中,此类帮助程序通常安装在 /usr/lib/APPLICATION/ 中,其中 APPLICATION 是应用程序或守护程序的名称.

If the application or daemon does not have root privileges, a helper program that is installed setuid root is needed. It is supplied with one or more device names. The helper program verifies each one is a character device using stat() and S_IFCHR(st_mode) (to avoid the helper being used in security exploits); and if they are, executes the above-mentioned command. In Linux, such helpers are usually installed in /usr/lib/APPLICATION/, where APPLICATION is the name of the application or daemon.

应用程序或守护进程本身可以通过 popen 执行帮助程序("/path/to/helper 2>/dev/null", "r"),并使用例如 fscanf() 读取输入.(记得使用 pclose() 获取状态,并使用例如 (WIFEXITED(status)&& !WEXITSTATUS(status)) 来验证命令是否成功.

The application or daemon itself can execute the helper via popen("/path/to/helper 2>/dev/null", "r"), and read the input using for example fscanf(). (Remember to use pclose() to obtain the status, and use e.g. (WIFEXITED(status) && !WEXITSTATUS(status)) to verify the command was successful.

请注意,辅助程序方法可以更轻松地从 Linux 移植到其他 POSIXy 系统,只需将辅助程序替换为适合新系统的辅助程序即可.即使它不是作为 setuid root 安装的,它也为系统维护人员提供了一个简单的挂钩来修改应用程序或服务的行为,如果需要的话.我个人强烈推荐使用辅助方法.

Note that the helper program approach allows easier portability to other POSIXy systems from Linux, by simply replacing the helper program with one appropriate to the new system. Even if it is not installed as setuid root, it gives system maintainers an easy hook to modify the behaviour of the application or service, if such need arises. I personally warmly recommend the helper approach.