分类: C/C++
2008-03-10 07:22:01
19.3 打开伪终端设备
在S V R 4和4 . 3 + B S D系统中打开伪终端设备的方法有所不同。我们提供两个函数来处理所有细节:p t y m _o p e n用来打开下一个有效的伪终端主设备, p t y s_o p e n用来打开相应的从设备。
# include "ourhdr.h"
int ptym_open (char * pts_ name) ;
返 回 : 若 成 功 则 为 伪 终 端 主 设 备 文 件 描 述 符 , 否 则 为 -1
int ptys_open (int fdm, char * pts_name ) ;
返 回 : 若 成 功 则 为 伪 终 端 从 设 备 文 件 描 述 符 , 否 则 为 -1
通常我们不直接调用这两个函数—函数p t y _ f o r k(见1 9 . 4节)调用它们并f o r k出一个子进程。
p t y m _ o p e n决定下一个有效的伪终端主设备并打开该设备。这个调用必须分配一个数组来存放主设备或从设备的名称,并且如果调用成功,相应的主设备或从设备的名称会通过p t s _ n a m e返回。这个名称和p t y m_o p e n返回的文件描述符将传给p t y s_o p e n,该函数用来打开一
个从设备。
在讲解p t y _ f o r k函数之后,使用两个函数来打开这两个设备的原因将会很明显。通常,一个进程调用ptym_open来打开一个主设备并且得到从设备的名称。该进程然后fork子进程,子进程在调用setsid 建立新的对话后调用ptys_open来打开从设备。这就是从设备如何成为子进程的控制终端的过程。
S V R 4系统下所有伪终端的流实现细节在AT & T〔1 9 9 0 d〕第1 2章中有所说明,还描述了以下三个函数:g r a n t p t ( 3 ),u n l o c k p t ( 3 ),和 p t s n a m e ( 3 )。
伪终端主设备是/ d e v / p t m x。这是一个流的增殖设备(clone device)。这意味着当我们打开该增殖设备,其open例程自动决定第一个未被使用的伪终端主设备并打开这个设备。(下一节将看到在伯克利系统中,我们必须自己找到第一个未被使用的伪终端主设备。)
程序19-1 SVR4的伪终端打开函数
#include
#include
#include
#include
#include
#include "ourhdr.h"
extern char *ptsname (int);
int ptym_open (char *pts_name)
{
char *ptr;
int fdm;
strcpy(pts_name, "/dev/ptmx");
if ((fdm = open (pts_name, O_RDWR)) < 0)
return (-1);
if (grantpt(fdm) < 0) {
close (fdm);
return (-2);
}
if (unlockpt (fdm) < 0) {
close (fdm);
return (-3);
}
if ((ptr = ptsname(fdm)) == NULL) {
close (fdm);
return (-4);
}
strcpy(pts_name, pty);
return (fdm);
}
int ptys_open (int fdm, char *pts_name)
{
int fds;
if ( (fds = open (pts_name, O_RDWR)) < 0) {
close (fdm);
return (-5);
}
if (ioctl(fds, I_PUSH, "ptem") < 0) {
close (fdm);
close (fds);
return (-6);
}
if (ioctl(fds, I_PUSH, "ldterm") < 0) {
close (fdm);
close (fds);
return (-7);
}
if (ioctl (fds, I_PUSH, "ttcompat") < 0) {
close (fdm);
close (fds);
return (-8);
}
return (fds);
}
首先打开设备/ d e v / p t m x并得到伪终端主设备的文件描述符。打开这个主设备自动锁定了对应的从设备。
然后调用g r a n t p t来改变从设备的许可权。执行如下操作:(a)将从设备的所有权改为有效用户I D;(b)将组所有权改为组t t y;(c)将许可权改为只允许用户-读,用户-写和组-写。将组所有权设置为t t y并允许组-写许可权是因为程序w a l l ( 1 )和w r i t e ( 1 )的设置-用户- I D为组t t y。调用函数g r a n t p t执行/ u s r / l i b / p t _ c h m o d。该程序的设置-用户- I D为r o o t,因此它能够修改从设备的所有者和许可权。
函数u n l o c k p t用来清除从设备的内部锁。在打开从设备前必须做这件事情。并且必须调用p t s n a m e来得到从设备的名称。这个名称的格式是/ d e v / p t s /N N N。
文件中接下来的函数是p t y s _ o p e n,该函数真正被用来打开一个从设备。在S V R 4系统中,如果调用者是一个还没有控制终端的对话期首进程, o p e n就会分配一个从设备作为控制终端。如果不希望函数自动做这件事,可以在调用时指明O _ N O C T T Y标志。
打开从设备后,将三个流模块放在从设备的流上。p t e m是伪终端虚拟模块, l d t e r m是终端行规程模块。这两个模块合在一起像一个真正的终端模块一样工作。t t co m p a t提供了向老系统如V 7、4 B S D和X e n i x 的 ioctl 调用的兼容性。这是一个可选的模块,但是因为它自动尝试控制台登录和网络登录(见程序1 2 - 1 0的输出),我们将其加到从设备的流中。
调用这两个函数的结果是得到:伪终端主设备的文件描述符和从设备的文件描述符。
19.3.2 4.3+BSD
在4 . 3 + B S D系统中必须自己来确定第一个可用的伪终端主设备。 为达到这个目的,从/ d e v / p t y p 0开始并不断尝试,直到成功打开一个可用的伪终端主设备或试完所有设备。在打开设备时,将看到两种可能的错误: E I O指设备已经被使用; E N O E N T表示设备不存在。对于后一种情况,可以停止搜索,因为所有的伪终端设备都在被使用中。一旦成功地打开一个例如名为/ d e v / p t y M N 的伪终端主设备,那么对应的从设备的名称为/ d e v / t t y M N。
程序1 9 - 2中的函数p t y s _ o p e n打开该从设备。我们在该函数中调用c h o w n和c h m o d,必须意识到调用这两个函数的进程必须有超级用户许可权。如果必须改变所有权,那么这两个函数调用必须放在一个设置-用户-ID的root用户的可执行函数中,这类似于4.3+BSD系统下的grantpt函数。
在4 . 3 + B S D系统之下打开p t y从设备不具有像分配作为控制终端的设备那样的副作用。下一节将探讨如何在4 . 3 + B S D系统下分配控制终端。
这个函数尝试1 6种不同的伪终端主设备:从/ d e v / p t y p 0到/ d e v / p t y T f。具体有效的
p t y设备号取决于两个因素:(a)内核中配置的号码;(b)/ d e v目录下的特殊文件号。对于任何程序来说,有效的号码是( a)和( b)中较小的一个。并且,即使(a)和(b)中小的值大于6 4,许多现有的B S D应用(如t e l n e t d,r l o g i n d等等)会搜索程序19-2中第一个f o r循环中的 p q r s。
程序19-2 4.3+BSD的伪终端o p e n函数
#include
#include
#include
#include
#include
#include "ourhdr.h"
int ptym_open (char *pts_name)
{
int fdm;
char *ptr1, *ptr2;
strcpy(pts_name, "/dev/ptyXY");
for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != 0; ptr1 ++){
pts_name [8] = *ptr1;
for (ptr2 = "0123456789adcdef"; *ptr2 != 0; ptr2 ++) {
pts_name [9] = *ptr2;
if ((fdm = open (pts_name, O_RDWR)) < 0){
if (errno = ENOENT)
return (-1);
else
continue;
}
pts_name[5] = 't';
return (fdm);
}
}
return (-1);
}
int ptys_open (int fdm, char *pts_name)
{
struct group *grptr;
int gid,fds;
if ( (grptr = getgrnam("tty")) != NULL)
gid = grptr->gr_gid;
else
gid = -1;
chown (pts_name, getuid(), gid);
chmod (pts_name, S_IRUSR |S_IWUSR |S_IWGRP);
if ( (fds = open(pts_name, O_RDWR)) < 0) {
close (fdm);
return (-1);
}
return (fds);
}