13

从串行设备(/dev/ttyXX)读取多个进程使得这两个进程无法获取所有数据——数据将以某种方式在它们之间拆分。我想编写一个从串行设备读取的程序,创建几个主/从 pty 对,然后允许从串行设备读取的程序改为从 pty 读取,以便所有读取进程都接收数据从串行设备开始,让 pty 像串行设备一样工作,因为当它们开始从 pty 读取时,它们只会获得最新的数据。换句话说,您不会得到任何在开始阅读之前写入的数据(根据我的经验,这就是 /dev/ttyXX 设备的工作方式,或者至少是我正在阅读的 RS-232 风速计)。命名管道可以通过捕获 SIGPIPE 来模拟这些语义,以确定没有读取器,因此我们可以选择不写入特定的命名管道。然而,一些为使用终端而编写的二进制文件在与命名管道通信时可能会失败,因为检查 isatty() 和 tcsetattr() 等调用时的 errno 条件可能会导致失败条件。这里的关键是能够使用为终端编写的现有二进制文件。

因此,如果我可以检测到何时打开 pty 的从属端进行读取,这应该给我提供与命名管道案例中没有 SIGPIPE 大致相同的语义。我注意到 HP-UX 将 TIOCTRAP 作为 ioctl() 命令,这似乎完全符合我的要求,但遗憾的是它在 Linux 上不可用。

几天来我一直在阅读参考资料,这类事情的选择数量是惊人的。答案可能在于终端设置、阻塞/非阻塞行为、在某处设置缓冲区大小、从 poll()/select() 报告的条件或某种组合。不过,我似乎找不到任何东西。我想知道是否有可能我需要编写自己的设备驱动程序,但似乎我应该能够做到这一点而无需走那么远。

因此,为了澄清:
- 问题是:如何检测有人在 Linux 中打开 pty(伪终端)的从属端?
- 我希望阅读器打开 pty 的从端以在阅读器打开 pty 后严格接收写入的数据(如果我的多写过程只是在阅读器打开从端之前写入数据一段时间,数据将缓冲起来并最终写入器将阻塞,从属读取器在打开时将立即获取所有缓冲数据——这是不可取的,因为我希望它只获取在紧邻时间附近生成的数据)
——它必须是一个 pty,而不是命名管道、套接字等,因为 isatty() 和 tcsetattr() 等需要正常,以便现有的二进制文件工作

4

2 回答 2

17

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

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

这会在 tty 主机上设置 HUP 标志。您现在定期轮询 HUP 标志poll()(例如,每当数据来自您的数据源时):

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()您的数据源,然后当数据可用时,同时poll()发送所有主 tty 并发送数据到其中任何一个没有POLLHUP设置。

于 2010-08-16T02:09:54.423 回答
4

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

于 2011-04-23T03:56:48.527 回答