【问题标题】:When pty [Pseudo terminal] slave fd settings are changed by "tcsetattr" , how can the master end capture this event without delay?当 pty [Pseudo terminal] slave fd 设置被 "tcsetattr" 改变时,master 端如何不延迟捕获这个事件呢?
【发布时间】:2014-03-05 16:35:53
【问题描述】:

slave fd 被另一个应用程序(比如“A”)用作串行端口设备。

A 将设置其波特率/停止位等。我的应用需要此信息。

顺便说一句,有没有办法让只打开主 fd 的进程收到所有发给从 fd 的 ioctl() 调用的通知?

【问题讨论】:

    标签: linux ioctl pty


    【解决方案1】:

    是的,这是可能的(在 Linux 和 2004 之前的 FreeBSD 中),在数据包模式下使用 pty 并在其上设置 EXTPROC 标志。

    • ioctl(master, TIOCPKT, &nonzero)在主端启用分组模式。现在主端的每个read 都会产生一个数据包:无论在另一端写入什么,加上一个状态字节,说明从端的情况(“终端(即从端)被刷新”)

    • 然而,这并不意味着主服务器立即知道从服务器端的变化,例如主服务器上的select()不会在这些更改发生后立即返回,只有当有需要阅读的内容时才返回

    • 但是,在 pty 的本地标志字中设置 EXTPROC 后 - 使用 tcsetattr() - select() 在从站状态更改后立即返回,然后可以检查从站termios - 直接通过我们在父进程中保持打开的从 fd,或者,至少在 linux 上,只需在主进程上通过 tcgetattr()

    • 请注意,EXTPROC 会禁用 pty 驱动程序的某些部分,例如本地回显已关闭。

    EXTPROC 没有被大量使用(here 是一个可能的用例),不是很便携并且根本没有记录(它至少应该在 linux termiostty_ioctl 手册页中的某个地方)。查看 linux 内核源代码 drivers/tty/n_tty.c 我得出结论,slave 的 termios 结构中的任何更改都会使主端上的 select() 返回 - 但不会更改任何内容的 tcsetattr() 不会。

    编辑:为了响应 J.F. Sebastians 在下面的请求,我给出了一个示例程序,该示例程序应该清楚地说明如何在 linux 机器上以数据包模式使用 EXTRPOC

    /* Demo program for managing a pty in packet mode with the slave's
    ** EXTPROC bit set, where the master gets notified of changes in the
    ** slaves terminal attributes
    **   
    ** save as extproc.c, compile with gcc -o extproc extproc.c -lutil
    */
    
    
    
    #include <stdio.h>
    #include <pty.h>
    #include <termios.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <sys/select.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    #define BUFSIZE 512
    
    void main() {
      int master;      // fd of master side
      pid_t pid;
      if((pid = forkpty(&master, NULL, NULL, NULL))) { // we're parent
        fd_set rfds, xfds;
        int retval, nread, status = 0, nonzero = 1;
        char buf[BUFSIZE];
    
        ioctl(master, TIOCPKT, &nonzero); // initiate packet mode - necessary to get notified of
                                          // ioctl() on the slave side
        while(1) {
          // set stdout unbuffered (we want to see stuff as it happens)                                                                                 
          setbuf(stdout, NULL);
    
          // prepare the file descriptor sets
          FD_ZERO(&rfds);
          FD_SET(master, &rfds);
    
          FD_ZERO(&xfds);
          FD_SET(master, &xfds);
    
          // now wait until status of master changes     
          printf("---- waiting for something to happen -----\n");
          select(1 + master, &rfds, NULL, &xfds, NULL);
    
          char *r_text = (FD_ISSET(master, &rfds) ? "master ready for reading" : "- ");
          char *x_text = (FD_ISSET(master, &xfds) ? "exception on master" : "- ");
    
          printf("rfds: %s, xfds: %s\n", r_text, x_text);
          if ((nread = read(master, buf, BUFSIZE-1)) < 0) 
            perror("read error");
          else {         
            buf[nread] = '\0';
            // In packet mode *buf will be the status byte , and buf + 1 the "payload"
            char *pkt_txt = (*buf &  TIOCPKT_IOCTL ? " (TIOCPKT_IOCTL)" : "");
            printf("read %d bytes: status byte %x%s, payload <%s>\n", nread, *buf, pkt_txt, buf + 1);
          }
          if (waitpid(pid, &status, WNOHANG) && WIFEXITED(status)) {
            printf("child exited with status %x\n", status);
            exit(EXIT_SUCCESS);
          } 
        }
      } else { // child
        struct termios tio;
    
        // First set the EXTPROC bit in the slave end termios structure
        tcgetattr(STDIN_FILENO, &tio);
        tio.c_lflag |= EXTPROC;
        tcsetattr(STDIN_FILENO, TCSANOW, &tio); 
    
        // Wait a bit and do an ordinary write()
        sleep(1);
        write(STDOUT_FILENO,"blah", 4);
    
        // Wait a bit and change the pty terminal attributes. This will be picked up by the master end
        sleep(1);
        tio.c_cc[VINTR] = 0x07; 
        tcsetattr(STDIN_FILENO, TCSANOW, &tio);
    
        // Wait a bit and exit
        sleep(1);
      } 
    }
    

    输出将类似于:

    ---- waiting for something to happen -----
    rfds: master ready for reading, xfds: exception on master
    read 1 bytes: status byte 40 (TIOCPKT_IOCTL), payload <>
    ---- waiting for something to happen -----
    rfds: master ready for reading, xfds: - 
    read 5 bytes: status byte 0, payload <blah>
    ---- waiting for something to happen -----
    rfds: master ready for reading, xfds: exception on master
    read 1 bytes: status byte 40 (TIOCPKT_IOCTL), payload <>
    ---- waiting for something to happen -----
    rfds: master ready for reading, xfds: - 
    read error: Input/output error
    child exited with status 0
    

    一般来说,pty 的主端不必是 tty(并且有一个关联的termios 结构),但在 Linux 下,它是(具有共享的termios:一端的更改将同时更改@ 987654340@ 结构)。

    也就是说我们可以修改上面的示例程序,在master端设置EXTPROC,不会有任何区别。

    据我所知,所有这些都没有记录,当然更不便携。

    【讨论】:

    • 您能否为答案中的陈述提供一些参考?您是如何通过 pty 了解 TIOCPKT、EXTPROC、select() 行为的?
    • @J.F.Sebastian:我通过包含一个示例程序修改了上面的答案。我不记得我是如何发现 TIOCPKTEXTPROC 的:它们在网络上几乎没有被提及(现在显然没有太多使用)
    • 1- 我有read error 第一行(由于标准输出缓冲。禁用它(setvbuf(stdout, NULL, _IONBF, 0);)会产生预期的输出)2- setting EXTPROC only in the parent doesn't produce notifications on my Linux machine
    • @J.F.Sebastian: 1 默认情况下,Linux 中的标准输出 should be line buffered。为了确定,我添加了setbuf(stdout, NULL)2 我认为您在此处犯了一个错误:您将EXTPROC 设置为stdin 而不是master
    • 不,TIOCPKT 必须在 master 上设置,但 EXTPROC 应该 像上面的示例代码一样在 slave 端配置。我只观察到在 linux 机器上,master 和 slave 似乎共享它们的 termios 结构(至少在当时),因此 EXTPROC 也可以在 master 端设置。所有这些都是无证且不可移植的,换句话说:不推荐!
    猜你喜欢
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    • 2016-06-22
    • 2021-12-31
    • 2011-07-18
    • 2016-08-01
    • 2020-07-20
    相关资源
    最近更新 更多