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

问题描述:

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

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.

因此,如果我可以检测到pty的从属端何时打开以进行读取,那么这应该给我大致相同的语义,与命名管道中没有SIGPIPE的语义相同.我注意到HP-UX将TIOCTRAP作为ioctl()命令,它似乎完全可以实现我想要的功能,但可惜的是,它在Linux上不可用.

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.

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

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.

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

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

找不到此原因的原因是,没有专门记录在案的接口允许它.但是,有一个技巧可以使您做到这一点.在打开伪终端主机(假设这里是文件描述符ptm)之后,打开并立即关闭从机端:

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));

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

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

如果阅读器消失了,将再次设置POLLHUP.

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

在您的情况下,您甚至可能甚至不需要从一个循环到下一个循环就记住给定的pty是否具有读取器-只需在数据源上的read()上进行阻止,然后在数据可用时同时您所有的主tty,并将数据发送给未设置POLLHUP的任何主tty.

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.