如何检测有人在 Linux 中打开 pty(伪终端)的从属端?

发布于 2024-09-14 20:58:07 字数 1021 浏览 7 评论 0原文

从串行设备(/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() 等需要正常,现有的二进制文件才能工作

Having more than one process read from a serial device (/dev/ttyXX) makes it so that both processes can't get all of the data -- the data will be split between them in some way. I'd like to write a program that reads from a serial device, creates several master/slave pty pairs, and then allows programs that were made to read from the serial device to instead read from the ptys so that all reading processes receive the data from the serial device and have the ptys act like the serial device in the sense that when they start reading from the pty they get only the most recent data. In other words, you won't get any data that was written before you started to read (it's my experience that this is how /dev/ttyXX devices work, or at least the RS-232 anemometer I'm reading from). Named pipes can mimic these semantics by trapping SIGPIPE to determine that there is no reader and thus we can choose not to write to that particular named pipe. However, some binaries that were written to use terminals may fail when talking to named pipes, as checks for isatty() and the errno condition on calls like tcsetattr() can cause failing conditions. The key here is to be able to use existing binaries that were written for a terminal.

So, if I can detect when the slave side of the pty is opened for reading, this should give me roughly the same semantics as there being no SIGPIPE in the named pipe case. I notice that HP-UX has TIOCTRAP as an ioctl() command which seems to do exactly what I want, but sadly it is not available on Linux.

I've been reading references for days and the number of options for this type of thing is staggering. The answer might lie in the terminal settings, blocking/non-blocking behavior, setting buffer sizes somewhere, conditions reported from poll()/select(), or some combination. I can't seem to find anything, though. I'm wondering if it's possible that I need to write my own device driver, but it seems like I should be able to do this without going that far.

So, for clarification:
- The question is: How can I detect when someone opens the slave side of a pty (pseudo-terminal) in Linux?
- I want a reader opening the slave side of the pty to receive data written strictly after the reader opens the pty (if my multi-writing process just writes data for a while before the reader opens the slave side, the data will buffer up and eventually the writer will block and the slave reader, upon opening, will immediately get all the buffered data -- this is not desirable as I want it to get only data generated in the immediate temporal vicinity)

- It must be a pty, not a named pipe, socket, etc, as isatty() and tcsetattr(), etc need to be OK so that existing binaries work

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

罗罗贝儿 2024-09-21 20:58:07

您找不到它的原因是因为没有专门允许它的记录接口。然而,有一个技巧可以让你做到这一点。打开伪终端主控(此处假设为文件描述符 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() ,然后在数据可用时,同时 poll() 所有主 tty,并将数据发送到任何未设置 POLLHUP 的 tty。

The reason you can't find this is because there's no documented interface specifically to allow it. However, there is a trick that allows you to do it. After opening the pseudo-terminal master (assumed here to be file descriptor ptm), you open and immediately close the slave side:

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

This sets the HUP flag on the tty master. You now poll the HUP flag regularly with poll() (say, whenever data comes in from your data source):

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 */
}

If the reader ever goes away, POLLHUP will be set again.

In your case, you probably don't even need to remember from one loop to the next whether a given pty has a reader - just block on read() on your data source, then when data is available, simultaneously poll() all of your master ttys and send the data to any of them that do not have POLLHUP set.

短暂陪伴 2024-09-21 20:58:07

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

Add an inotify watch on the slave pty and poll on that. You can get an inotify event on open. You can then poll on the inotify file descriptor and the master pty file descriptor. You can get an inotify event for open (IN_OPEN). This will unblock the poll when the slave side is opened.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文