📄 00000019.htm
字号:
<HTML><HEAD> <TITLE>BBS水木清华站∶精华区</TITLE></HEAD><BODY><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER>发信人: SuperSB (孤鹰), 信区: Linux <BR>标 题: [转载]unix环境高级编程-19 <BR>发信站: BBS 水木清华站 (Wed Mar 15 14:32:07 2000) <BR> <BR> <BR> <BR> <BR>发信人: taosm (128+64-->cool), 信区: unix <BR>标 题: unix环境高级编程--第19章 伪终端 <BR>发信站: 西十八BBS (Sat Mar 11 14:24:07 2000), 转信 <BR> <BR> <BR>第十九章 伪终端 <BR>19.1 引言 <BR>在第九章中我们介绍了进行终端登录时,需要通过一个终端设备自动提供终端的语 <BR>义。在终端和运行程序之间有一个终端行规程(图11.2),通过这个规程我们能够 <BR>在终端上设置特殊字符(退格、行删除、中断等)。但是,当一个登录请求到达网 <BR>络连接时,终端行规程并不是自动被加载到网络连接和登录程序shell之间的。图 <BR>9.5显示了有一个伪终端设备驱动程序被用来提供终端语义。 <BR> 除了用于网络登录,伪终端还被用在其他方面,我们将在本章中进行介绍。我们 <BR>将首先提供在SVR4和4.3+BSD系统下用于创建伪终端的函数,然后使用这些函数编 <BR>写一个程序用来调用pty。我们将看到这个程序的不同使用:在输入字符和终端显 <BR>示之间进行转换(BSD的码转换程序)和运行协同进程来避免我们在程序14.10中遇 <BR>到的缓冲区问题。 <BR>19.2 概述 <BR>伪终端这个名词暗示了与一个应用程序相比,它更加象一个终端。但事实上,伪终 <BR>端并不是一个真正的终端。图19.1显示了使用伪终端的进程的典型结构。其中关键 <BR>点如下: <BR>图19.1 使用伪终端的典型进程结构 <BR>1 通常一个进程打开伪终端主设备然后调用fork。子进程建立了一个新的会话,打 <BR>开一个相应的伪终端从设备,将它复制成标准输入、标准输出和标准出错,然后调 <BR>用exec。伪终端从设备成为子进程的控制终端。 <BR>2 对于伪终端从设备之上的用户进程来说,其标准输入、标准输出和标准出错都能 <BR>当作终端设备使用。用户进程能够调用11章中讲到的所有输入/输出函数。但是因 <BR>为在伪终端从设备之下并没有真正的设备,无意义的函数调用(改变波特率、发送 <BR>中断符、设置奇偶校验等)将被忽略。 <BR>3 任何写到伪终端主设备的输入都会作为在从设备端的输入,反之亦然。事实上所 <BR>有从设备端的输入都来自于主设备上的用户进程。这看起来就象一个流管道(图1 <BR>5.3),但从设备上的终端行规程使我们拥有普通管道之外的其他处理能力。 <BR>图19.1显示了BSD系统中的伪终端结构。在19.3.2中我们将看到如何打开这些设备 <BR>。 <BR>在SVR4系统中伪终端是使用流系统来创建的(12.4节)。图19.2详细描述了SVR4系 <BR>统中各个伪终端模块之间的关系。在虚线框中的两个流模块是可选的。请注意在从 <BR>设备上的三个流模块同12.10网络登录程序的输出是一样的。在19.3.1小节中将介 <BR>绍如何组织这些流模块。 <BR> 从现在开始将简化以上图示,首先我们不再画出图19.1的"读、写功能"或图19.2 <BR>的流首。我们还使用缩写"pty"表示伪终端,并将图19.2中所有伪终端从设备之上 <BR>的流模块集合表示为"终端行规程"模块。 <BR>图19.2 在SVR4下的伪终端组织结构 <BR> 现在来看一下伪终端的几种典型用法。 <BR>网络登录服务器 <BR> 伪终端用于构造网络登录服务器。典型的例子是telnetd和rlogind服务器。在St <BR>evens[1990]的第15章中详细讨论了提供rlogin服务的步骤。一旦登录shell运行在 <BR>远端主机上,我们得到如图19.3的结构。同样的结构也用于telnetd服务器。 <BR>在rlogind服务器和登录shell之间有两个exec调用,这是因为login程序通常是在 <BR>两个exec之间检验用户是否合法的。 <BR>图19.3 rlogind服务器的进程组织结构 <BR>本图的一个关键点是驱动伪终端主设备的进程通常同时在读写另一个输入/输出流 <BR>。在本例中另一个输入/输出流是TCP/IP。这表示该进程必然使用了某种形式的如 <BR>select或poll那样的输入/输出多路转接(节12.5),或被分成两个进程。请回忆 <BR>我们在18.7节讨论过的一个进程和两个进程的的比较。 <BR>script程序 <BR>script程序是随SVR4和4.3+BSD提供的,该程序将终端会话期间所有的输入和输出 <BR>信息在一个文件中做一个拷贝。它通过将自己置于终端和登录shell的一个新的调 <BR>用之间来完成这个工作。图19.4详细描述了script程序相关的交互。这里我们特别 <BR>指出script程序通常是从登录shell启动的,该shell然后等待程序的结束。 <BR>当script程序在运行的时候,在伪终端从设备之上终端行规程的所有输出都被复制 <BR>到一个script文件中(通常叫做typescript)。因为我们的击键通常被行规程的模 <BR>块回显,该script文件也包括了输入的内容。但是,因为口令字不被回显,该scr <BR>ipt文件不会包含口令字。 <BR>图19.4 script程序 <BR>本书中所有运行程序并显示其输出的实例都是由script程序实现的,这样避免了手 <BR>动拷贝程序输出可能带来的错误。 <BR>在19.5节开发一个通用的pty程序后,我们将看到一个巧妙的shell程序能够将它转 <BR>化成一个script程序。 <BR>expect程序 <BR> 伪终端可以用来使交互式的程序运行在非交互的状态中。许多程序需要一个终端 <BR>来运行,18.7节中的call进程就是一个例子。它假定标准输入是一个终端并在启动 <BR>时将其设置为初始模式(18.20程序)。该程序不能从一个shell程序中被运行来自 <BR>动拨号到远程系统,登录,取出信息和登出。 <BR> 同修改所有交互式程序来支持批处理模式的操作比较,一个更好的解决方法是提 <BR>供一种手段来通过一个script来驱动交互式程序。expect程序[Libes 1990;1991] <BR>提供了这样的方法。类似于19.5节的pty程序,它使用伪终端来运行其他程序。但 <BR>是,expect还提供了一种编程语言用于检查程序的输出来确定以什么作为输入发送 <BR>给该程序。当一个交互式的程序开始从一个script运行时,我们不能仅仅是将scr <BR>ipt中的所有内容输入到程序中去。相应的,我们要通过检查程序的输出来决定下 <BR>一步输入的内容。 <BR>运行协同进程 <BR> 在14.10的程序例子中,我们不能调用使用标准输入/输出库进行输入、输出的协 <BR>同进程,这是因为当我们通过管道与协同进程进行通讯时,标准输入/输出库会将 <BR>标准输入、输出的内容放到缓冲区中,从而引起死锁。如果协同进程是一个已经编 <BR>译的程序而我们又没有源程序,我们就无法在源程序中加入fflush语句来解决这个 <BR>问题。图14.9显示了一个进程驱动协同进程的情况。我们需要做的是将一个伪终端 <BR>放到两个进程之间,如图19.5所示。 <BR>图19.5 用伪终端驱动一个协同进程 <BR> 现在协同进程的标准输入和标准输出就象一个终端设备一样,所以标准输入/输出 <BR>库会将这两个流设置为行缓冲的。 <BR>父进程有两种不同的方法在自身和协同进程之间获得伪终端(这种情况下的父进程 <BR>可以象程序14.9,使用两个管道和协同进程进行通讯;或者象程序15.1那样,使用 <BR>一个流管道)。一个方法是父进程直接调用pty_fork函数(19.4节)而不是fork。 <BR>另一种方法是exec该pty程序,将协同进程作为参数(19.5节)。我们将在说明pt <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -