当有人打开Linux中pty(伪终端)的从端口时,我该如何检测?

13

如果多个进程从串行设备(/dev/ttyXX)读取数据,那么这些进程无法获取所有数据 -- 数据将以某种方式在它们之间分配。我想编写一个程序,从串行设备中读取数据,创建多个主/从pty对,然后允许原本为串行设备而编写的程序从ptys读取数据,使得所有读取进程都能接收到来自串行设备的数据,并且在开始从pty读取时,pty会像串行设备一样只提供最新的数据。换句话说,在你开始读取之前不会获取到任何已经写入的数据(根据我的经验,这是/dev/ttyXX设备的工作方式,或者至少是我正在读取的RS-232风速仪的方式)。命名管道可以通过捕获SIGPIPE来模仿这些语义,以确定是否有读取器,因此我们可以选择不向特定的命名管道写入。然而,当使用终端写入二进制文件时,一些检查isatty()和调用tcsetattr()的errno条件可能导致失败。关键在于能够使用已经针对终端编写的现有二进制文件。

所以,如果我可以检测到打开了pty的从端以进行读取,这应该给我与命名管道情况下没有SIGPIPE的大致相同的语义。我注意到HP-UX具有TIOCTRAP作为ioctl()命令,它似乎完全符合我的要求,但可悲的是它在Linux上不可用。

我已经阅读了几天的参考资料,这种类型的选项数量惊人。答案可能在终端设置、阻塞/非阻塞行为、在某个地方设置缓冲区大小、来自poll()/select()的条件报告或某些组合中。尽管如此,我似乎找不到任何线索。我想知道是否有可能需要编写自己的设备驱动程序,但是这似乎不必要。

因此,为了澄清:
- 问题是:如何检测Linux中的pty(伪终端)的从端何时被打开?

我希望在从属pty的读取端打开后,只接收严格在读取端打开后写入的数据(如果我的多进程写入过程在读取器打开从属端之前仅写入数据,则数据将缓冲并最终导致写入器阻塞,而打开从属读取器后,将立即获取所有缓冲数据 -- 这是不可取的,因为我只想获取在短时间内生成的数据)。
它必须是一个pty,而不是命名管道、套接字等,因为isatty()和tcsetattr()需要正常工作,以便现有的二进制文件可以工作。
2个回答

20

你找不到这个的原因是因为没有专门允许它的文档接口。但是,有一个技巧可以让你做到这点。在打开伪终端主控制器(这里假设文件描述符为ptm)后,你需要打开并立即关闭从属端:

close(open(ptsname(ptm), O_RDWR | O_NOCTTY));

这将在 tty 主设备上设置 HUP 标志。现在,您可以使用 poll() 定期轮询 HUP 标志(例如,每当来自数据源的数据到达时):

struct pollfd pfd = { .fd = ptm, .events = POLLHUP };
poll(&pfd, 1, 10 /* or other small timeout */);

if (!(pfd.revents & POLLHUP))
{
    /* There is now a reader on the slave side */
}
如果读取器消失了,POLLHUP 会再次设置。 在你的情况下,你可能甚至不需要记住从一个循环到另一个循环中是否有读取器连接到给定的pty - 只需要在数据源上阻塞 read(),然后当可用数据时,同时在所有主tty上进行 poll() 并将数据发送到任何没有设置POLLHUP 的tty。

谢谢您的解释。我在“socat”的源代码中发现了这个技巧,但没有意识到我必须打开并关闭从设备端口。即使理解了这个技巧,我仍然有一些抓狂的时刻,因为我使用了openpty()而不是显式地打开/dev/ptmx,这给了我一个单独的、打开的fd用于pts,因此我最初无法获得HUP条件。调用openpty(),然后关闭从它返回的从设备fd就足够了。再次感谢您的回复。您是如何了解这个未记录的功能的? - Nate Sanders
我想我是通过在Usenet上发布帖子找到了它。这个问题应该让未来的人更容易在谷歌上搜索到! - caf

4

在从pty上添加一个inotify监视器,并对其进行轮询。您可以在打开时获取inotify事件。然后,您可以轮询inotify文件描述符和主pty文件描述符。您可以获取打开的inotify事件(IN_OPEN)。当从端口被打开时,这将解除轮询的阻塞。


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