伪终端是指对于一个应用程序而言,他看上去像一个终端,但事实上它并不是一个真正的终端。
- 进程打开伪终端设备,然后fork。子进程建立一个新的会话,打开一个相应的伪终端从设备。复制输入、输出和标准错误文件描述符,调用exec,子进程从设备编程伪终端。
- 伪终端能像终端一样,但是无意义的函数调用如改变波特率、发送中断符、设置奇偶校验将被忽略。
- 伪终端可以做输入和输出。
posix_opent函数提供了一种可移植的方法来打开下一个可用伪终端主设备
#include <stdlib.h> #include <fcntl.h> int posix_openpt(int flags); 返回值:成功返回下一个可用的PTY主设备文件描述符,出错-1 flags:设备操作标记,可以是0或者以下两项的之一,O_RDWR允许对设备同时进行读写操作,此标记通常需要指定
O_NOCTTY不将设备作为进程的控制终端
在伪终端从设备可用之前,它的权限必须设置,以便应用程序可以访问它。
改变指定master对应从设备的属主与访问权限
#define _XOPEN_SOURCE #include <stdlib.h> int grantpt(int fd);
int unlockpt(int fd);
返回值:成功0,出错-1
fd:文件描述符
ptsname返回PTY从设备的名字
#define _XOPEN_SOURCE #include <stdlib.h> char *ptsname(int fd); #define _GNU_SOURCE #include <stdlib.h> int ptsname_r(int fd, char *buf, size_t buflen);
fd:文件描述符
apue写的函数,打开下一个可用的PTY主设备。调用者必须分配一个数组来存放主设备或从设备名字。
#include "apue.h" int ptym_open(char *pts_name, int pts_namesz); 返回值:成功返回PTY主设备文件描述符,出错-1 int ptys_open(char *pts_name);
返回值:成功返回PTY设备文件描述符,出错-1
pts_name:打开设备的名字
pts_namesz:缓冲区字节长度
实现函数:
#include "apue.h" #include <errno.h> #include <fcntl.h> #if defined(SOLARIS) #include <stropts.h> #endif int ptym_open(char *pts_name, int pts_namesz) { char *ptr; int fdm, err; if((fdm = posix_openpt(O_RDWR)) < 0) return(-1); if(grantpt(fdm) < 0) goto errout; if(unlock(fdm) < 0) goto errout; if((ptr = ptsname(fmd)) == NULL) goto errout; strncpy(pts_name, ptr, pts_namesz); pts_name[pts_namesz - 1] = ‘\0‘; return(fdm); errout: err = errno; close(fdm); errno = err; return(-1); } int ptys_open(char *pts_name) { int fds; #if defined(SOLARIS) int err, setup; #endif if((fds = open(pts_name, O_RDWR)) < 0) goto errout; if(setup == 0) { if(ioctl(fds, I_PUSH, "ptem") < 0) goto errout; if(ioctl(fds, I_PUSH, "ldterm") < 0) goto errout; if(ioctl(fds, I_PUSH, "ttcompat") < 0) { errout: err = errno; close(fds); errno = err; return(-1); } } #endif return(fds); }
用fork调用打开主设备和从设备,创建作为会话首进程的子进程并使其具有控制终端。
#include "apue.h" #include <termios.h> pid_t pty_fork(int *ptrfdm, char *slave_name, int slave_namesz, const struct termios *slave_termios, const struct winsize *slave_winsize);
pty_fork函数
1 #include "apue.h" 2 #include <termios.h> 3 4 pid_t pty_fork(int *ptrfdm, char *slave_name, int slave_namesz, 5 const struct termios *slave_termios, 6 const struct winsize *slave_winsize) 7 { 8 int fdm, fds; 9 pid_t pid; 10 char pts_name[20]; 11 12 if((fdm = ptym_open(pts_name, sizeof(pts_name))) < 0) 13 err_sys("can‘t open master pty: %s, error %d", pts_name, fdm); 14 15 if(slave_name != NULL) { 16 strncpy(slave_name, pts_name, slave_namesz); 17 slave_name[slave_namesz - 1] = ‘\0‘; 18 } 19 20 if((pid = fork()) < 0) 21 return(-1); 22 else if(pid == 0) { /* childe */ 23 if(setsid() < 0) 24 err_sys("setsid error"); 25 26 if((fds = ptys_open(pts_name)) < 0) 27 err_sys("can‘t open slave pty"); 28 close(fdm); 29 #if defined(BSD) 30 if(ioctl(fds, TIOCSCTTY, (char *)0) < 0) 31 err_sys("TIOCSCTTY error"); 32 #endif 33 if(slave_termios != NULL) { 34 if(tcsetattr(fds, TCSANOW, slave_termios) < 0) 35 err_sys("tcsetattr error on slave pty"); 36 } 37 if(slave_winsize != NULL) { 38 if(ioctl(fds, TIOCSWINSZ, slave_winsize) < 0) 39 err_sys("TIOCSWINSZ error on slave pty"); 40 } 41 42 if(dup2(fds, STDIN_FILENO) != STDIN_FILENO) 43 err_sys("dup2 error to stdin"); 44 if(dup2(fds, STDOUT_FILENO) != STDOUT_FILENO) 45 err_sys("dup2 error to stdout"); 46 if(dup2(fds, STDERR_FILENO) != STDERR_FILENO) 47 err_sys("dup2 error to stderr"); 48 if(fds != STDIN_FILENO && fds != STDOUT_FILENO && 49 fds != STDERR_FILENO) 50 close(fds); 51 return(0); 52 } else { 53 *ptrfdm = fdm; 54 return(pid); 55 } 56 }