APUE 伪终端

《UNIX环境高级编程》第19章 伪终端 笔记

概述

伪终端这个术语是指,对于一个应用程序而言,它看上去像一个终端,但事实上它并不是个真正的终端。

图中的关键点是:

  1. 通常,一个进程打开伪终端主设备,然后调用fork。子进程建立一个新的会话,打开一个相应的伪终端从设备,将其文件描述符复制到标准输入、标准输出和标准错误,然后调用exec。伪终端从设备成为子进程的控制终端。
  2. 对于伪终端从设备上的用户进程来说,其标准输入、标准输出和标准错误都是终端设备。通过这些描述符,用户进程能够处理所有终端I/O函数。但是因为仍终端从设备不是真正的终端设备,所以无意义的函数调用(例如,改变波特率、发送中断符、设置奇偶校验)将被忽略
  3. 任何写到伪终端主设备的都会作为从设备的输入,反之亦然。事实上,所有从设备端的输入都来自于伪终端主设备上的用户进程。这看起来就像一个双向管道,但是从设备上的中断行规程使我们拥有管道没有的处理奇谈问题的能力

伪终端的某些经典用途:

网络登录服务器

伪终端可用于构造提供网络登录的服务器。典型的例子是 telnetd和rlogind服务器

在rlogind服务器和登录shell之间有两个exec调用,这是因为login程序通常是在两个exec之间检验用户是否合法

窗口系统终端模拟

窗口系统通常提供一个终端模拟器,这样我们就能在熟悉的命令行环境中通过shell来运行程序。终端模拟器作为shell和窗口管理器之间的媒介。每个shell在自己的窗口中执行。

shell将自己的标准输入、标准输出、标准错误连接到PTY的从设备端。终端模拟器程序打开PTY的主设备。终端模拟器除了作为窗口子系统的接口,还要负责模拟一种特殊的终端,这意味着它需要根据它所模拟的设备类型来响应返回码。这些码列在 termcap和 terminfo数据库中。

script 程序

script(1)程序是随大多数UNIX系统提供的,它将终端会话期间的所有输入和输出信息复制到一个文件中。为完成此工作,该程序将自己置于终端和一个新调用的登录 shell之间。这里要特别指出, script程序通常是从登录shell启动的,该 shell还要等待 script程序的终止。

expect 程序

伪终端可以用来在非交互模式中驱动交互式程序的运行。许多硬连线程序需要一个终端才能行, passwd(1)命令就是一个例子,它要求用户在系统提示后输入口令

运行协同进程

如果协同进程是一个已经编译的程序而我们又没有源程序,则无法在源程序中加入ff1ush语句来解决这个问题。我们需要做的是将一个伪终端放到两个进程之间,诱使协同进程认为它是由终端驱动的,而非另一个进程

观看长时间运行程序的输出

使用任何一个标准 shell,可以将一个需要长时间运行的程序放到后台运行。但是,如果将该程序的标准输出重定向到一个文件,并且它产生的输出又不多,那么我们就不能方便地监控程序的进展,因为标准I/O库将完全缓冲它的标准输出。

如果有源程序,则可以加入fflush调用强制标准I/O缓冲区在某些节点冲洗或者把缓冲模式改使用 setybuf的行缓冲。然而,如果没有源程序,可以在pty程序下运行该程序,让标准I/O库认为标准输出是终端。

打开伪终端设备

PTY表现得就像物理终端设备一样,因此应用程序就无须在意它们在使用的是何种设备。各种平台打开伪终端设备的方法有所不同。posix_openpt函数提供了一种可移植的方法来打开下一个可用伪终端主设备

1
2
3
4
5
#include <stdlib.h> #include <fcntl.h>
int posix_openpt(int oflag);
// Returns: file descriptor of next available PTY master if OK, −1 on error

在伪终端从设备可用之前,它的权限必须设置,以便应用程序可以访问它。 grantpt函数提供这样的功能:它把从设备节点的用户ID设置为调用者的实际用户ID,设置其组ID为一非指定值,通常是可以访问该终端设备的组。权限被设置为:对个体所有者是读写,对组所有者是写(0620)

1
2
3
4
5
6
#include <stdlib.h>
int grantpt(int fd);
int unlockpt(int fd);
// Both return: 0 on success, −1 on error

unlockpt函数用于准予对伪终端从设备的访问,从而允许应用程序打开该设备。阻止其他进程打开从设备后,建立该设备的应用程序有机会在使用主、从设备之前正确地初始化这些设备

注意,在 grantpt和unlockpt这两个函数中,文件描述符参数是与伪终端主设备关联的文件描述符。

如果给定了伪终端主设备的文件描述符,那么可以用 ptsname函数找到伪终端从设备的路径名。这使应用程序可以独立于给定平台的某种特定约定而标识从设备。注意,该函数返回的名字可能存储在静态存储中,因此后续的调用可能会覆盖它。

1
2
3
4
5
#include <stdlib.h>
char *ptsname(int fd);
// Returns: pointer to name of PTY slave if OK, NULL on error

高级特性

伪终端还有其他特性:

  1. 打包模式

打包模式(packet mode)能够使PTY主设备了解到PTY从设备的状态变化

  1. 远程模式

PTY主设备可以用 TIOCREMOTE ioct1命令将PTY从设备设置成远程模式

  1. 窗口大小变化

PTY主设备上的进程可以用 TIOCSWINSZ ioct1命令来设置从设备的窗口大小。如果新的大小和当前的大小不同, SIGWINCH信号将被发送到PTY从设备的前台进程组。

  1. 信号发生

读、写PTY主设备的进程可以向PTY从设备的进程组发送信号