Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2038712
  • 博文数量: 414
  • 博客积分: 10312
  • 博客等级: 上将
  • 技术积分: 4921
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-31 01:49
文章分类

全部博文(414)

文章存档

2011年(1)

2010年(29)

2009年(82)

2008年(301)

2007年(1)

分类: C/C++

2008-03-10 07:23:04

19.4 pty_fork函数

现在使用上一节介绍的两个函数: p t y m _ o p e np t y s _ o p e n,编写我们称之为p t y _ f o r k的函数。这个新函数具有如下功能:打开主设备和从设备,建立作为对话期管理者的子进程并使其具有控制终端。

# include

#include

#include  /* 4.3+BSD defines struct winsize here */

#include "ourhdr.h"

pid_t pty_fork (int  * ptrfdm,  char  * slave_name,

const struct termios  * slave_termios,

const struct winsize  * slave_winsize) ;

              0                       ID,若          - 1

p t y主设备的文件描述符通过p t r f d m指针返回。

如果s l a v e _ n a m e不为空,从设备的名称被存放在该指针指向的存储区中。调用者必须为该存储区分配空间。如果指针s l a v e _ t e r m i o s不为空,该指针所引用的结构将初始化从设备的终端行规程。如果该指针为空,系统将从设备的t e r m i o s结构初始化为一个由具体应用定义的初始状态。类似的,如果s l a v e _ w i n s i z e指针不为空,该指针所引用的结构将初始化从设备的窗口大小。如果该指针为空,w i n s i z e结构通常被初始化为0

程序1 9 - 3显示了这个程序的代码。调用相应的p t y m _ o p e np t y s _ o p e n函数,这个函数在S V R 44 . 3 + B S D系统下都可以使用。

在打开伪终端主设备后, f o r k将被调用。正如前面提到的,要等到调用setid建立新的对话期后才调用ptys_open。当调用setsid时,子进程还不是一个进程组的首进程(想一想为什么?)

因此9 . 5节列出的三个操作被使用:(a)子进程作为对话的管理者创建一个新的对话期;( b)子进程创建一个新的进程组;(c)子进程没有控制终端。在S V R 4系统中,当调用p t y s _ o p e n时,从设备成为了控制终端。在4 . 3 + B S D系统中,必须调用ioctl并使用参数T I O C S C T T Y来分配一个控制终端。然后termiosw i n s i z e这两个结构在子进程中被初始化。最后从设备的文件描述符被复制到子进程的标准输入、标准输出和标准出错中。这表示由子进程所e x e c的进程都会将上述三个句柄同伪终端从设备联系起来。在调用f o r k后,父进程返回伪终端主设备的描述符并返回。下一节将在p t y程序中使用pty _ fork

程序19-3 pty_fork函数

#include

#include

#ifndef TIOCGWINSZ

#include

#endif

#include "ourhdr.h"

 

pid_t pty_fork (int *ptrfdm, char *slave_name, const struct termios *slave_termios, const struct winsize *slave_winsize)

{

      int fdm, fds;

      pid_t pid;

      char pts_name[20];

     

      if ( (fdm = ptym_open(pts_name)) <0)

           err_sys("can't open master pty: %s ", pts_name);

      if (slave_name != NULL)

           strcpy(slave_name,pts_name);

      if ( (pid = fork ()) < 0)

           return (-1);

      else if (pid == 0){

           if (setsid() < 0)

                 err_sys("setsid error");

           if ((fds = pty_open (fdm,pts_name)) < 0)

                 err_sys ("can't open slave pty");

           close (fdm);

#if define(TIOCSTTY) && !define (CIBAUD)

           if (ioctl (fds, TIOCSTTY, (char *) 0 ) < 0)

                 err_sys("TIOCSTTTY error");

#endif

           if (slave_termios ! = NULL){

                 if (tcsetattr (fds, TCSANOW, slave_termios) < 0)

                      err_sys("tcsetattr error on slave pty");

           }

           if (slave_winsize != NULL){

                 if (iioctl (fds, TIOCSWINSZ, slave_winsize) < 0)

                      err_sys("TIOCSWINSZ error on slave pty");

           }

           if (dup2(fds, STDIN_FILENO) != STDIN_FILENO)

                 err_sys ("dup2 error on stdin");

           if (dup2(fds, STDOUT_FILENO) != STDOUT_FILENO)

                 err_sys ("dup2 error on stdout");

           if (dup2(fds, STDERR_FILENO) != STDERR_FILENO)

                 err_sys ("dup2 error on stderr");

           if (fds > STDERR_FILENO)

                 close (fds);

           return (0);

      }

      else {

           *ptrfdm = fdm;

           return (pid);

      }

}

 

19.5 pty程序

编写p t y程序的目的是为了用键入:pty prog arg1 arg2 来代替:prog arg1 arg2, 这样使我们可以用p t y来执行另一个程序,该程序在一个自己的对话期中执行,并和一个

伪终端连接

看一下p t y程序的源码。程序1 9 - 4包含m a i n函数。它调用上一节的p t y _ f o r k函数。

程序19-4 pty程序的m a i n函数

#include

#include

#ifndef TIOCGWINSZ

#include

#endif

#include "ourhdr.h"

 

static void set_noecho (int );   /*at the end of this file */

void do_driver (char *);

void loop(int , int);

 

int ain (int argc, char *argv[])

{

      int            fdm, c, ignoreeof, interactive, noecho, verbose;

      pid_t   pid;

      char    *driver, slave_name[20];

      struct termios orig_termios;

      struct winsize size;

     

      interactive = isatty (STDIN_FILENO);

      ignoreeof = 0;

      noecho = 0;

      verbose = 0;

      driver = NULL;

     

      opterr = 0;

      while ((c = getopt (argc, argv, "d:einv")) != EOF) {

           switch (c) {

           case 'd':

                 driver =optarg;

                 break;

           case 'e':

                 noecho = 1;

                 break;

           case 'i':

                 ignoreeof = 1;

                 break;

           case 'n':

                 interactive = '0';

                 break;

           case 'v':

               verbose = 1;

               break;

           case '?':

                 err_quit ("unrecognize option: -%c", optopt);

           }

      }

      if (optind >= argc)

           err_quit ("Usage: pty [-d drive -einv] progra [arg ...]");

      if (interactive)

      {

           if (togetattr (STDIN_FILENO, &orig_termios) < 0)

                 err_sys ("tcgetattr error on stdin");

           if (ioctl(STDIN_FILENO, TIOCGWINSZ, (char *) &size) < 0)

                 err_sys ("TIOCGWINDSZ error");

           pid = pty_fork (&fdm, slave_name, &orig_termios, &size); 

      }

      else

           pid = pty_fork (&fdm, slave_name, NULL, NULL);

      if (pid < 0)

           err_sys ("fork error");

      else if (pid == 0){

           if (noecho)

                 set_noecho(STDIN_FILENO);

           if (execvp(argv(optind), &argv[optind]) < 0)

                 err_sys("can't execute: %s ", argv[optind]);

      }

      if (verbose){

           fprintf(stderr, "slave name = %s \n", slave_name);

           if (driver != NULL)

                 fprintf(stderr, "driver = %s \n",driver);

      }

      if (interactive && driver == NULL) {

           if (tty_raw (STDIN_FILENO) < 0)

                 err_sys("tty_raw error");

            if (atexit(tty_atexit) < 0)

                 err_sys("atexit error");

      }

      if (driver )

           do_driver (driver);

      loop(fdm, ignoreeof);

      exit (0);

}

 

static void set_noecho (int fd) 

{

      struct termios stermios;

      if (tcgetattr(fd, &sterios) < 0)

           err_sys("tcgetattr error");

      stermios.c_lflag &= ~(ECHO | ECHOE |ECHOK |ECHONL);

      stermios.c_oflag & = ~(ONLCR);

      if (tcsetattr (fd, TCSANOW, &stermios) < 0)

           err_sys("tcsetattr error");

}

下一节检测p t y程序的不同使用时,将会探讨多种的行命令选择项。

调用p t y _ f o r k前,我们取得了t e r m i o sw i n s i z e结构的值,将其传递给p t y _ f o r k。通过这种方法,伪终端从设备具有和现在的终端相同的初始状态

p t y _ f o r k返回后,子进程关闭了伪终端从设备的回显,并调用e x e c v p来执行命令行指定的程序。所有的命令行参数将成为程序的参数

父进程在调用e x i t时执行原先设置的退出处理程序,它复原终端状态,将用户终端设置为

初始模式(可选)。下一节将讨论d o _ d r i v e r函数。

接下来父进程调用函数l o o p(见程序1 9 - 5)。该函数仅仅是将所有标准输入拷贝到伪终端主设备并将伪终端主设备接收到的所有内容拷贝到标准输出。同1 8 . 7节一样,我们有两个选择一个进程还是两个进程?为了有所区别,这里使用两个进程,尽管使用s e l e c tp o l l的单进程也是可行的

程序19-5 loop函数

#include

#include

#include "ourhdr.h"

 

#define BUFSIZE 512

 

static void sig_term (int);

static volatile sig_atomic_t sigcaught; /*set by signal handler */

 

void loop (int ptym, int igoreeof)

{

      pid_t child;

      int nread;

      char buff[BUFSIZE];

     

      if ((child = fork ()) < 0)

           err_sys("fork error");

      else if (child == 0){

           `for (;;){

                 if ((nread = read (STDIN_FILENO, buff, BUFFSIZE)) < 0)

                      err_sys("read error from stdin");

                 else if (nread == 0)

                      break;

                 if (writen(ptym,buf,nread) != nread)

                      err_sys("writen error to aster pty");

           }

           if (ignoreeof == 0)

                 kill (getpid (), SIGTERM);

           exit (0);

      }

     

      if (signal_intr(SIGTERM, sig_term) == SIG_ERR)

           err_sys("signal_intr error for SIGTERM");

     

      for (;;) {

           if ((nread = read(ptym,buff,BUFFSIZE)) <= 0)

                 break;

           if (writen (STDOUT_FILENO,buff, nread) != nread)

                 err_sys("writen error to stdout");

      }

      if (sigcaught == 0)

           kill (child, SIGTERM);

      return ;

}

 

static void sig_term(int signo)

{

      sigcaught = 1;

      return ;

}

 

注意,当使用两个进程时,如果一个终止,那么它必须通知另一个。我们用S I G T E R M

行这种通知。

阅读(1029) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~