📄 19.htm
字号:
<p>程序19.1 SVR4的伪终端打开函数 </p>
<p>我们首先打开设备/dev/ptmx并得到伪终端主设备的文件描述符。打开这个</p>
<p>主设备 </p>
<p>自动锁定了对应的从设备。 </p>
<p>我们然后调用grantpt来改变从设备的权限。执行如下操作:(a)将从设备</p>
<p>的所 </p>
<p>有权改为有效用户ID;(b)将组所有权改为组tty;(c)将权限改为只允许</p>
<p>user </p>
<p>-read,user-write和group-write。将组所有权设置为tty并允许</p>
<p>group-write权限 </p>
<p>是因为程序wall(1)和write(1)的组标识符被设置为组tty。调用函数</p>
<p>grantpt执行 </p>
<p>/user/lib/pt_chmod。该程序的用户被设置为root因此它能够修改从设备</p>
<p>的所有者 </p>
<p>和权限。 </p>
<p>函数unlockpt用来清除从设备的内部锁。在打开从设备前我们必须做这件事</p>
<p>情。 </p>
<p>并且我们必须调用ptsname来得到从设备的名称。这个名称的格式是</p>
<p>/dev/pts/NNN </p>
<p>。 </p>
<p>文件中接下来的函数是ptys_open,该函数真正被用来打开一个从设备。在</p>
<p>SVR4系 </p>
<p>统中,如果调用者是一个还没有控制终端的会话,open就会分配一个从设备</p>
<p>作为控 </p>
<p>制终端。如果我们不希望函数自动做这件事,可以在调用时指明O_NOCTTY标</p>
<p>志。 </p>
<p>在打开从设备后,我们将三个流模块放在从设备的流上。Ptem是"伪终端"虚</p>
<p>拟模 </p>
<p>块,ldterm是终端行规程模块。这两个模块合在一起象一个真正的终端模块</p>
<p>一样工 </p>
<p>作。ttcompat提供了向老系统如V7、4BSD和Xenix的ioctl调用的兼容性。</p>
<p>这是一个 </p>
<p>可选的模块,但是因为它自动尝试控制台登录和网络登录(见程序12.10的输</p>
<p>出) </p>
<p>,我们将其加到从设备的流中。 </p>
<p>调用这两个函数的结果是得到:伪终端主设备的文件描述符和从设备的文件描</p>
<p>述 </p>
<p>符。 </p>
<p>19.3.2 4.3+BSD </p>
<p>在4.3+BSD系统中我们必须自己来确定第一个可用的伪终端主设备。为达到这</p>
<p>个目 </p>
<p>的,我们从/dev/ptyp0开始并不断尝试直到成功打开一个可用的伪终端主设</p>
<p>备或试 </p>
<p>完所有设备。在打开设备的时候,我们会看到两种可能的错误:EIO指设备已</p>
<p>经被 </p>
<p>使用;ENOENT表示设备不存在。在后一种情况,我们可以停止搜索,因为所</p>
<p>有的伪 </p>
<p>终端设备都在被使用中。一旦我们成功打开一个例如名为/dev/ptyMN的伪终</p>
<p>端主设 </p>
<p>备,那么对应的从设备的名称为/dev/ttyMN。 </p>
<p>程序19.2中的函数ptys_open打开该从设备。我们在该函数中调用chown和</p>
<p>chmod, </p>
<p>必须意识到调用这两个函数的进程必须有超级用户的权限。如果必须改变权限</p>
<p>标志 </p>
<p>,那么这两个函数必须放在一个set_user_ID的root用户的可执行程序中,</p>
<p>这类似 </p>
<p>于4.3+BSD系统下的grantpt函数。 </p>
<p>在4.3+BSD系统之下打开pty从设备不具有象分配作为控制终端的设备那样的</p>
<p>副作用 </p>
<p>。我们将在下一节探讨如何在4.3+BSD系统下分配控制终端。 </p>
<p>这个函数尝试16个不同的伪终端主设备:从/dev/ptyp0到/dev/ptyTf。具</p>
<p>体有效的 </p>
<p>pty设备号取决于两个因素:(a)在内核中配置的号码;(b)在/dev目录</p>
<p>下的特 </p>
<p>殊文件号。对于任何程序来说,有效的号码是(a)和(b)中较小的一个。</p>
<p>并且, </p>
<p>即使(a)和(b)中小的值大于64,许多现有的BSD应用(telnetd,</p>
<p>rlogind,等 </p>
<p>等)会搜索程序19.2中第一个for循环中的"pqrs"。 </p>
<p>_______________________________________________________________________</p>
<p>________ </p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <errno.h> </p>
<p>#include <fcntl.h> </p>
<p>#include <grp.h> </p>
<p>#include "ourhdr.h" </p>
<p>使用;ENOENT表示设备不存在。在后一种情况,我们可以停止搜索,因为所</p>
<p>有的伪 </p>
<p>终端设备都在被使用中。一旦我们成功打开一个例如名为/dev/ptyMN的伪终</p>
<p>端主设 </p>
<p>备,那么对应的从设备的名称为/dev/ttyMN。 </p>
<p>程序19.2中的函数ptys_open打开该从设备。我们在该函数中调用chown和</p>
<p>chmod, </p>
<p>必须意识到调用这两个函数的进程必须有超级用户的权限。如果必须改变权限</p>
<p>标志 </p>
<p>,那么这两个函数必须放在一个set_user_ID的root用户的可执行程序中,</p>
<p>这类似 </p>
<p>于4.3+BSD系统下的grantpt函数。 </p>
<p>在4.3+BSD系统之下打开pty从设备不具有象分配作为控制终端的设备那样的</p>
<p>副作用 </p>
<p>。我们将在下一节探讨如何在4.3+BSD系统下分配控制终端。 </p>
<p>这个函数尝试16个不同的伪终端主设备:从/dev/ptyp0到/dev/ptyTf。具</p>
<p>体有效的 </p>
<p>pty设备号取决于两个因素:(a)在内核中配置的号码;(b)在/dev目录</p>
<p>下的特 </p>
<p>殊文件号。对于任何程序来说,有效的号码是(a)和(b)中较小的一个。</p>
<p>并且, </p>
<p>即使(a)和(b)中小的值大于64,许多现有的BSD应用(telnetd,</p>
<p>rlogind,等 </p>
<p>等)会搜索程序19.2中第一个for循环中的"pqrs"。 </p>
<p>_______________________________________________________________________</p>
<p>________ </p>
<p>#include <sys/types.h> </p>
<p>#include <sys/stat.h> </p>
<p>#include <errno.h> </p>
<p>#include <fcntl.h> </p>
<p>#include <grp.h> </p>
<p>#include "ourhdr.h" </p>
<p>int </p>
<p>ptym_open(char *pts_name) </p>
<p>{ </p>
<p>int fdm; </p>
<p>char *ptr1, *ptr2; </p>
<p>strcpy(pts_name, "/dev/ptyXY"); </p>
<p>/* array index: 0123456789 (for references in following</p>
<p>code) */ </p>
<p>for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != 0; ptr1++) { </p>
<p>pts_name[8] = *ptr1; </p>
<p>for (ptr2 = "0123456789abcdef"; *ptr2 != 0; ptr2++) { </p>
<p>pts_name[9] = *ptr2; </p>
<p>/* try to open master */ </p>
<p>if ( (fdm = open(pts_name, O_RDWR)) < 0) { </p>
<p>if (errno == ENOENT) /* different from EIO */ </p>
<p>return(-1); /* out o </p>
<p>pty devices */ </p>
<p>else </p>
<p>continue; /* try n </p>
<p>xt pty device */ </p>
<p>} </p>
<p>pts_name[5] = 't'; /* change "pty" to "tty" */ </p>
<p>return(fdm); /* got it, return fd of master * </p>
<p>} </p>
<p>} </p>
<p>return(-1); /* out of pty devices */ </p>
<p>} </p>
<p>int </p>
<p>ptys_open(int fdm, char *pts_name) </p>
<p>{ </p>
<p>struct group *grptr; </p>
<p>int gid, fds; </p>
<p>if ( (grptr = getgrnam("tty")) != NULL) </p>
<p>gid = grptr->gr_gid; </p>
<p>else </p>
<p>gid = -1; /* group tty is not in the group file */ </p>
<p>/* following two functions don't work unless we're root </p>
<p>/ </p>
<p>chown(pts_name, getuid(), gid); </p>
<p>chmod(pts_name, S_IRUSR | S_IWUSR | S_IWGRP); </p>
<p>if ( (fds = open(pts_name, O_RDWR)) < 0) { </p>
<p>close(fdm); </p>
<p>return(-1); </p>
<p>} </p>
<p>return(fds); </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>________ </p>
<p>程序19.2 4.3+BSD系统下的伪终端open函数 </p>
<p>19.4 pty_fork函数 </p>
<p>现在我们使用上一节中的两个函数:ptym_open和ptys_open,编写我们称</p>
<p>之为pty </p>
<p>_fork的函数。这个新函数具有了如下功能:打开主设备和从设备,建立作为</p>
<p>会话 </p>
<p>管理者的子进程并使其具有控制终端。 </p>
<p>#include <sys/types.h> </p>
<p>#include <termios.h> </p>
<p>#include <sys/ioctl.h> /* 4.3+BSD系统中,在这里定义了winsize结</p>
<p>构 */ </p>
<p>#include <ourhdr.h> </p>
<p>pid_t pty_fork(int *ptrfdm, char *slave_name, </p>
<p>const struct termios *slave_termios, </p>
<p>const struct winsize *slave_winsize); </p>
<p>返回:在子进程返回0;在父进程返回子进程的进程ID;遇到错误返回-1
</p>
<p>Pty主设备的文件描述符通过ptrfdm指针返回。 </p>
<p>如果slave_name不为空,从设备的名称被存放在该指针指向的存储区中。调</p>
<p>用者必 </p>
<p>须为该存储区分配空间。 </p>
<p>如果指针slave_termios不为空,该指针所引用的结构将从设备的终端行规</p>
<p>程初始 </p>
<p>化。如果该指针为空,系统将从设备的termios结构初始化为一个由具体应用</p>
<p>定义 </p>
<p>的初始状态。类似的,如果slave_winsize指针不为空,该指针所引用的结</p>
<p>构将从 </p>
<p>设备的窗口大小初始化。如果该指针为空,winsize结构通常被初始化为0。</p>
<p>程序19.3显示了这个程序的代码。调用相应的ptym_open和ptys_open函</p>
<p>数,这个函 </p>
<p>数在SVR4和4.3+BSD系统下都可以使用。 </p>
<p>在打开伪终端主设备后,fork将被调用。正如前面提到的,我们要等到调用</p>
<p>setid </p>
<p>建立新的会话后才调用ptys_open。当调用setid时,子进程还不是一个进程</p>
<p>组的l </p>
<p>eader(想一想为什么?)因此第9.5节列出的三个操作被使用:(a)子进</p>
<p>程作为 </p>
<p>对话的管理者创建一个新的对话;(b)子进程创建一个新的进程组;(c)</p>
<p>子进程 </p>
<p>没有控制终端。在SVR4系统中,当调用ptys_open时,从设备成为了控制终</p>
<p>端。在 </p>
<p>4.3+BSD系统中,我们必须调用ioctl并使用参数TIOCSCTTY来分配一个控制</p>
<p>终端。 </p>
<p>然后termios和winsize这两个结构在子进程中被初始化。最后从设备的文件</p>
<p>描述符 </p>
<p>被复制到子进程的标准输入、标准输出和标准出错中。这表示由子进程所</p>
<p>exec的进 </p>
<p>程都会将上述三个句柄同伪终端从设备联系起来。 </p>
<p>在调用fork后,父进程返回伪终端主设备的描述符并返回。在下一节我们将</p>
<p>在pty </p>
<p>程序中使用pty_fork。 </p>
<p>_______________________________________________________________________</p>
<p>________ </p>
<p>#include <sys/types.h> </p>
<p>#include <termios.h> </p>
<p>#ifndef TIOCGWINSZ </p>
<p>#include <sys/ioctl.h> /* 44BSD requires this too */ </p>
<p>#endif </p>
<p>#include "ourhdr.h" </p>
<p>pid_t </p>
<p>pty_fork(int *ptrfdm, char *slave_name, </p>
<p>const struct termios *slave_termios, </p>
<p>const struct winsize *slave_winsize) </p>
<p>{ </p>
<p>int fdm, fds; </p>
<p>pid_t pid; </p>
<p>char pts_name[20]; </p>
<p>if ( (fdm = ptym_open(pts_name)) < 0) </p>
<p>err_sys("can't open master pty: %s", pts_name); </p>
<p>if (slave_name != NULL) </p>
<p>strcpy(slave_name, pts_name); /* return name of slave */ </p>
<p>if ( (pid = fork()) < 0) </p>
<p>return(-1); </p>
<p>else if (pid == 0) { /* child */ </p>
<p>if (setsid() < 0) </p>
<p>err_sys("setsid error"); </p>
<p>/* SVR4 acquires controlling terminal on </p>
<p>open() */ </p>
<p>if ( (fds = ptys_open(fdm, pts_name)) < 0) </p>
<p>err_sys("can't open slave pty"); </p>
<p>err_sys("dup2 error to stderr"); </p>
<p>if (fds > STDERR_FILENO) </p>
<p>close(fds); </p>
<p>return(0); /* child returns 0 just like fork() */ </p>
<p>} else { /* parent */ </p>
<p>*ptrfdm = fdm; /* return fd of master */ </p>
<p>return(pid); /* parent returns pid of child */ </p>
<p>} </p>
<p>} </p>
<p>_______________________________________________________________________</p>
<p>________ </p>
<p>程序19.3 pty_fork函数 </p>
<p>19.5 pty程序 </p>
<p>写pty程序的目的是为了用键入 </p>
<p>pty prog arg1 arg2 </p>
<p>来代替 </p>
<p>prog arg1 arg2 </p>
<p>这样使我们可以用pty来执行另一个程序,该程序在一个自己的会话中执行,</p>
<p>并和 </p>
<p>一个伪终端连接。 </p>
<p>让我们看以下pty程序的源代码。程序19.4包含main函数。它调用上一节的</p>
<p>pty_f </p>
<p>ork函数。 </p>
<p>_______________________________________________________________________</p>
<p>________ </p>
<p>#include <sys/types.h> </p>
<p>#include <termios.h> </p>
<p>#ifndef TIOCGWINSZ </p>
<p>#include <sys/ioctl.h> /* 44BSD requires this too */ </p>
<p>#endif </p>
<p>#include "ourhdr.h" </p>
<p>static void set_noecho(int); /* at the end of this file */ </p>
<p>void do_driver(char *); /* in the file driver.c */ </p>
<p>void loop(int, int); /* in the file loop.c */ </p>
<p>int </p>
<p>main(int argc, char *argv[]) </p>
<p>{ </p>
<p>int fdm, c, ignoreeof, interactive, noecho, </p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -