全部博文(95)
分类: LINUX
2008-07-12 18:15:33
这两天一直在叽叽复叽叽的念叨着伪终端的相关机制。一直不懂,或者是很多东西在脑子里横冲直撞。好不容易走到了最后,就这么放弃实属可惜。但是把一大本书看下来,的确也花了我很多的时间和精力,不能就因为这么点坎而让这项工程留有遗憾。其实也在网上找了相关的资料,但是我觉得解释的都不透彻,也有可能是我智拙,总之是得不到我想要的东西。后来无奈,只能从头再来,现在也算是略知一二吧。
WHY? 对,有好好的终端存在为什么要引入伪终端,其实这一概念的提出是有历史的渊源的,也算是网络技术发展所促吧。我们知道在终端登录时,是经由自动提供语义的终端设备进行的。在终端和运行的程序之间有一个终端行规程,通过它我们能够在终端上设置特殊字符,然后系统能按这些语义进行相应的操作。这是固定终端时的情形,因为init进程知道哪些设备(有与此相关的终端行规程)可用来进行登录,并为每个设备生成一个getty 进程。但是,在网络的情况下,所有登录都经由内核的网络接口驱动程序,事先并不知道将会有多少这样的登录,所以当一个登录请求到达时,终端行规程并不是自动被加载到网络连接和登录shell之间,原因就是不能事先确定终端行规程。所以引入伪终端,其来提供终端语义。就这一点而言,可以引申,相当于对输入的数据做预处理。有了这一点共识,相信对于伪终端其他方面的应用就好理解多了。
那么纵观伪终端的应用来看,对于PTY,一般都是利用了它的二方面的性质:
(1)是提供终端语义功能,这是最基本的,这一点可以引申为应用数据的预处理。
(2)终端的性质,我们知道对于终端I/O来说,一般标准库函数会提供行缓冲,以达到对数据尽量即时的输出。
先来看几个概念:
(1) PTY与pty:
PTY是一个设备,它分为主设备和从设备。一般先打开主设备,为从设备设定权限和解锁后才能实现对从设备的引用。主设备与从设备是成对实现的,它们是由克隆设备来实现的,其底层一般是基于STREAMS的管道。
而pty是一个程序,一般父进程执行pty的主程序,其打开主设备,再fork一个作为会话首进程的子进程并使其具有控制终端,打开从设备,并将子进程的标准输入/出,和出错都指向从设备。这样,pty父进程和子进程之间就通过主从设备低层的管理连接在一起了。这样就建立了:终端输入/出<->pty父进程<->主设备<->从设备<->pty子进程。从以上的结构可以看出(1)可以将pty父进程这一块看成是对输入/出的一个预处理。(2)从设备<->pty子进程,可以将从设备看成终端,实现某些终端功能。
(2) 终端与PTY 主设备、PTY从设备:
终端:就是一般的终端,是指真正的终端。
PTY主设备:一般是一个克隆设备,在每个系统中,有其特有的名字,我们每次按这样的名字打开时,就把它看成是已经存在的一个设备,但是这个设备怎么实现的不用用户去详细了解。一般与pty父进程相关联,由父进程将终端的输入读到这个主设备中,或是将这个主设备中的数据写到终端上去。
PTY从设备:也是一个克隆设备,与主设备是成对存在的,在每个系统中,也有其特有的名字,当我们每次以这样的名字打开时,就把它看成是一个已经客观存在的设备,不用考虑这个设备的具体实现。不同与主设备,它一般模拟的是一个带有终端行规程的终端,可以提供终端语义。
(3) 主从设备之间的实现:这是由系统的克隆设备提供的,由系统来实现,这两者之间一般是由STREAMS的管道来实现这两个之间数据的交换。
PTY的应用,我不想讲述具体的应用,只想大概谈论下每种应用类型所采用的PTY思想,以及采用了这样的方式解决了它们以前存在的哪些问题。
(1) 网络登录服务器:这个不用说,是始祖了,其实就是实现登录时终端语义的提供功能。
(2) script程序:基本上利用了数据预处理的理念,在终端的输入/出和实际的处理之间将数据通过一种方式先处理下,再让其输入和输出。
(3) expect程序:可以用来在非交互模式中驱动交互式程序的运行。有些程序(比如交互式,或是一种脚本的交互式)需要一个终端才能运行,则让伪终端从设备扮演了终端的角色,促使pty子进程的运行。同时在pty父进程中做变化,不是让脚本中的所有内容都输入到程序中,或将程序的输出送至脚本,而是要检查程序的输出,从而决定下一步输入的内容。
(4) 运行协同进程:通过一般的pipe连接的协同进程,不能使用标准I/O库进行输入或是输出。因为当通过管道与协同进程进行通信时,标准I/O库会将标准输入和输出的内容放到缓冲区中,从而引起死锁,而如果协同进程是一个已经编译的程序而我们又没有源程序时,则无法在源程序中加入fflush语句来解决这个问题。而将一个伪终端放到两个进程之间,这诱使协同进程认为它是由终端而非另一个进程驱动的,这样协同进程的标准输入和输出就像终端设备一样,所以标准I/O库将这两个流设置为行缓冲,可以尽量即时的输出和输入了。这就是利用了终端行具有行缓冲的理念,促使协同进程尽早的实现数据的I/O。
(5) 观看长时间运行程序的输出:这个就不用说了,道理同于上一个,就是利用了终端行缓冲性质,让标准I/O库认为标准输出是终端,这样能尽早的实现输出。