博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Unix环境高级编程(二十)伪终端
阅读量:6907 次
发布时间:2019-06-27

本文共 10049 字,大约阅读时间需要 33 分钟。

1、综述

  伪终端对于一个应用程序而言,看上去像一个终端,但事实上伪终端并不是一个真正的终端。从内核角度看,伪终端看起来像一个双向管道,而事实上Solaris的伪终端就是用STREAMS构建的。伪终端总是成对地使用的,就好像是个管道的两端。一端的设备称为"主设备"(master),另一端的设备称为"从设备"(slave),每一对伪终端设备,例如/dev/ptys0和/dev/ttys0,就好像是通过一个管道连在一起,其"从设备"一端与普通的终端设备没有什么区别,而"主设备"一端则跟管道文件相似。

伪终端的用途:

(1)构造网络登录服务器,例如telnetd和rlogind服务器。

(2)script程序,将终端会话的所有输入和输出信息复制到一个文件中,自己置于终端和登录shell的一个新调用之间。

(3)expect程序,伪终端可以在非交互模式中驱动交互程序的运行

(4)运行协同进程

(5)观看长时间运行程序的输出

2、打开伪终端设备

  各种平台打开伪终端设备的方法有所不同,posix_openpt用来打开一个可用的伪终端主设备,该函数可移植的。伪终端从设备可被使用之前,必须设置它的权限,调用grantpt函数设置权限,使得应用程序可以访问它。unlockpt函数批准读伪终端从设备的访问,从而允许应用程序打开该设备。ptsname函数找到从伪终端设备的路径名。函数原型如下:

#include <stdlib.h>

#include <fcntl.h>
int posix_openpt(int oflag); //成功返回下一个可以用的PTY主设备的文件描述符,出错返回-1
int grantpt(int fildes); //更改从PTY设备的权限
int unlockpt(int fildes) //允许从PTY设备被打开
char *ptsname(int fildes); //成功返回指向PTY从设备名的指针,出错返回NULL

写个程序调用以上函数获取一个可用的PTY主设备描述符,然后获取该主设备的从伪终端的路径名。程序如下:

1 #define _XOPEN_SOURCE 2 #include 
3 #include
4 #include
5 #include
6 7 int main() 8 { 9 int masterfd,slavefd;10 char *slavedevice;11 if((masterfd = posix_openpt(O_RDWR|O_NOCTTY)) == -1)12 {13 perror("posix_openpt() error");14 exit(-1);15 }16 if(grantpt(masterfd) == -1)17 {18 perror("grantpt() error");19 exit(-1);20 }21 if(unlockpt(masterfd) == -1)22 {23 perror("unlockpt() error");24 exit(-1);25 }26 if((slavedevice=ptsname(masterfd)) == NULL)27 {28 perror("ptsname() error");29 exit(-1);30 }31 printf("slave device is: %s\n", slavedevice);32 exit(0);33 }

程序执行结果如下:

 打开一个终端,输入tty 这个命令来查看当前所使用的终端名。参考自 

介绍另外两个函数ptym_open和ptys_open,前者用于打开下一个可用的PTY主设备,后者打开相应的从设备。这两个函数需要自己实现,函数声明如下所示:

int ptym_open(char *pts_name,int pts_namesz);

int pyts_open(char *pts_name);

3、基于STREAMS的伪终端

  基于STREAMS的PTY主克隆设备是/dev/ptmx。打开该设备,其克隆open例程自动决定第一个未被使用的PTY主设备,并打开这个设备。ptym_open和ptys_open实现如下所示:

1 #define _XOPEN_SOURCE 2 #include 
3 #include
4 #include
5 #include
6 #include
7 #include
8 9 int ptym_open(char *pts_name,int pts_namesz)10 {11 char *ptr;12 int fdm;13 strncpy(pts_name,"/dev/ptmx",pts_namesz);14 pts_name[pts_namesz-1] = '\0';15 if((fdm = open(pts_name,O_RDWR)) < 0)16 return -1;17 if(grantpt(fdm) < 0)18 {19 close(fdm);20 return -2;21 }22 if(unlockpt(fdm) < 0)23 {24 close(fdm);25 return -3;26 }27 if((ptr = ptsname(fdm)) == NULL)28 {29 close(fdm);30 return -4;31 }32 strncpy(pts_name,ptr,pts_namesz);33 pts_name[pts_namesz-1] = '\0';34 return (fdm);35 }36 int ptys_open(char *pts_name)37 {38 int fds,setup;39 if((fds = open(pts_name,O_RDWR)) < 0)40 return -5;41 return fds;42 }43 int main()44 {45 int fdm,fds;46 char slave_name[20];47 fdm = ptym_open(slave_name,sizeof(slave_name));48 if(fdm<0)49 {50 perror("ptym_open() error");51 exit(-1);52 }53 printf("open master device successfully.\n");54 printf("slave device name is:%s\n",slave_name);55 fds = ptys_open(slave_name);56 if(fds < 0)57 {58 perror("ptys_open() error");59 exit(-1);60 }61 printf("open slave device successfully.\n");62 exit(0);63 }

测试结果如下:

 4、基于BSD的伪终端

   需要自己确定第一个可用的PTY主设备,主设备名为/dev/ptyAX(/dev/ptys0),这里的A表示16个字母"pqrstuvwxyQPRST"中的一个,X则为16个16进制数字(0~f)之一,这样一共可以256个伪终端主设备。从/dev/ptyp0开始不断尝试,直到成功打开一个可用的PTY主设备或试完了所有设备。ptym_open和ptys_open实现如下所示:

1 #define _XOPEN_SOURCE  2 #include 
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 12 #ifndef _HAS_OPENPT 13 int posix_openpt(int oflag) 14 { 15 int fdm; 16 char *ptr1,*ptr2; 17 char ptm_name[16]; 18 19 strcpy(ptm_name,"/dev/ptyXY"); 20 for(ptr1="pqrstuvwxyzPQRST"; *ptr1 != 0; ptr1++) 21 { 22 ptm_name[8] = *ptr1; 23 for(ptr2="0123456789abcdef";*ptr2 != 0;ptr2++) 24 { 25 ptm_name[9] = *ptr2; 26 //try to open the master 27 if((fdm = open(ptm_name,oflag)) < 0) 28 { 29 if(errno == ENOENT) 30 return -1; 31 else 32 continue; 33 } 34 return fdm; 35 } 36 } 37 errno = EAGAIN; 38 return -1; 39 } 40 #endif 41 42 #ifndef _HAS_PTSNAME 43 char *ptsname(int fdm) 44 { 45 static char pts_name[16]; 46 char *ptm_name; 47 ptm_name = ttyname(fdm); 48 if(ptm_name == NULL) 49 return NULL; 50 strncpy(pts_name,ptm_name,sizeof(pts_name)); 51 pts_name[sizeof(pts_name)-1] = '\0'; 52 if(strncmp(pts_name,"/dev/pty",9) == 0) 53 pts_name[9] = 's'; 54 else 55 pts_name[5] = 't'; 56 return pts_name; 57 } 58 #endif 59 60 #ifndef _HAS_GRANTPT 61 int grantpt(int fdm) 62 { 63 struct group *grptr; 64 int gid; 65 char *pts_name; 66 67 pts_name = ptsname(fdm); 68 if((grptr = getgrnam("tty")) != NULL) 69 gid = grptr->gr_gid; 70 else 71 gid = -1; 72 if(chown(pts_name,getuid(),gid) < 0) 73 return -1; 74 return chmod(pts_name,S_IRUSR | S_IWUSR | S_IWGRP); 75 } 76 #endif 77 78 #ifndef _HAS_UNLOCKPT 79 int unlockput(int fdm) 80 { 81 return 0; 82 } 83 #endif 84 int ptym_open(char *pts_name,int pts_namesz) 85 { 86 char *ptr; 87 int fdm; 88 strncpy(pts_name,"/dev/ptyXX",pts_namesz); 89 pts_name[pts_namesz-1] = '\0'; 90 if((fdm = posix_openpt(O_RDWR)) < 0) 91 return -1; 92 if(grantpt(fdm) < 0) 93 { 94 close(fdm); 95 return -2; 96 } 97 if(unlockput(fdm) < 0) 98 { 99 close(fdm);100 return -3;101 }102 if((ptr = ptsname(fdm)) < 0)103 {104 close(fdm);105 return -4;106 }107 strncpy(pts_name,ptr,pts_namesz);108 pts_name[pts_namesz-1] = '\0';109 return fdm;110 }111 int ptys_open(char *pts_name)112 {113 int fds,setup;114 if((fds = open(pts_name,O_RDWR)) < 0)115 return -5;116 return fds;117 }

 5、基于Linux的伪终端

  Linux支持访问伪终端的BSD方法,也支持使用/dev/ptmx的克隆风格的伪终端接口。在Linux中PTY从设备以为组tty所拥有,ptym_open和ptys_open实现如下所示:

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 11 #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ 12 #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ 13 14 #ifndef _HAS_OPENPT 15 int posix_openpt(int oflag) 16 { 17 int fdm; 18 fdm = open("/dev/ptmx",oflag); 19 return fdm; 20 } 21 #endif 22 23 #ifndef _HAS_PTSNAME 24 char *ptsname(int fdm) 25 { 26 static char pts_name[16]; 27 int sminor; 28 if(ioctl(fdm,TIOCGPTN,&sminor) < 0) 29 return NULL; 30 snprintf(pts_name,sizeof(pts_name),"/dev/pts/%d",sminor); 31 return pts_name; 32 } 33 #endif 34 35 #ifndef _HAS_GRANTPT 36 int grantpt(int fdm) 37 { 38 char *pts_name; 39 pts_name = ptsname(fdm); 40 return chmod(pts_name,S_IRUSR | S_IWUSR | S_IWGRP); 41 } 42 #endif 43 44 #ifndef _HAS_UNLOCKPT 45 int unlockput(int fdm) 46 { 47 int lock = 0; 48 return (ioctl(fdm,TIOCSPTLCK,&lock)); 49 } 50 #endif 51 int ptym_open(char *pts_name,int pts_namesz) 52 { 53 char *ptr; 54 int fdm; 55 strncpy(pts_name,"/dev/ptmx",pts_namesz); 56 pts_name[pts_namesz-1] = '\0'; 57 if((fdm = posix_openpt(O_RDWR)) < 0) 58 return -1; 59 if(grantpt(fdm) < 0) 60 { 61 close(fdm); 62 return -2; 63 } 64 if(unlockput(fdm) < 0) 65 { 66 close(fdm); 67 return -3; 68 } 69 if((ptr = ptsname(fdm)) < 0) 70 { 71 close(fdm); 72 return -4; 73 } 74 strncpy(pts_name,ptr,pts_namesz); 75 pts_name[pts_namesz-1] = '\0'; 76 return fdm; 77 } 78 int ptys_open(char *pts_name) 79 { 80 int fds,setup; 81 if((fds = open(pts_name,O_RDWR)) < 0) 82 return -5; 83 return fds; 84 } 85 int main() 86 { 87 int fdm,fds; 88 char slave_name[20]; 89 fdm = ptym_open(slave_name,sizeof(slave_name)); 90 if(fdm<0) 91 { 92 perror("ptym_open() error"); 93 exit(-1); 94 } 95 printf("open master device successfully.\n"); 96 printf("slave device name is:%s\n",slave_name); 97 fds = ptys_open(slave_name); 98 if(fds < 0) 99 {100 perror("ptys_open() error");101 exit(-1);102 }103 printf("open slave device successfully.\n");104 exit(0);105 }

执行结果如下所示:

 6、pty_fork函数

  函数功能:用fork调用打开主设备和从设备,创建作为会话首进程的子进程并使其具有控制终端。函数声明如下:

#include <termios.h>

#include <sys/ioctl.h>
pid_t ptt_fork(int *ptrfdm,char *slave_name,int slave_names,const struct termiosz *slave_termios,const struct winsize *slave_winsize);

函数实现如下所示:

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 #ifndef IIOCGWINSZ 12 #include
13 #endif 14 pid_t ptt_fork(int *ptrfdm,char *slave_name,int slave_namesz, 15 const struct termios *slave_termios, 16 const struct winsize *slave_winsize) 17 { 18 int fdm,fds; 19 pid_t pid; 20 char pts_name[20]; 21 22 if((fdm=ptym_open(pts_name,sizeof(pts_name))) < 0) 23 { 24 perror("ptym_open()error"); 25 exit(-1); 26 } 27 if(slave_name != NULL) 28 { 29 strncpy(slave_name,pts_name,slave_namesz); 30 slave_name[slave_namesz-1] = '\0'; 31 } 32 if((pid = fork()) < 0) 33 { 34 perror("fork() error"); 35 exit(-1); 36 } 37 else if(pid == 0) 38 { 39 if(setsid() < 0) 40 { 41 perror("setsid() error"); 42 exit(-1); 43 } 44 if((fds = ptys_open(pts_name)) < 0) 45 { 46 perror("ptys_open() error"); 47 exit(-1); 48 } 49 close(fdm); 50 #if defined (TIOCSCTTY) 51 if(ioctl(fds,TIOCSCTTY,(char *)0) < 0) 52 { 53 perror("TIOCSCTTY error"); 54 exit(-1); 55 } 56 #endif 57 if(slave_termios != NULL) 58 { 59 if(tcsetattr(fds,TCSANOW,slave_termios) < 0) 60 { 61 perror("tcsetattr error on slave pty"); 62 exit(-1); 63 } 64 } 65 if(slave_winsize != NULL) 66 { 67 if(ioctl(fds,TIOCSWINSZ,slave_winsize) < 0) 68 { 69 perror("TIOCSWINSZ error on slave pty"); 70 exit(-1); 71 } 72 } 73 74 if(dup2(fds,STDIN_FILENO) != STDIN_FILENO) 75 { 76 perror("dups error to stdin"); 77 exit(-1); 78 } 79 if(dup2(fds,STDOUT_FILENO) != STDOUT_FILENO) 80 { 81 perror("dups error to stdout"); 82 exit(-1); 83 } 84 if(dup2(fds,STDERR_FILENO) != STDERR_FILENO) 85 { 86 perror("dups error to stderr"); 87 exit(-1); 88 } 89 if(fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO) 90 close(fds); 91 return 0; 92 } 93 else 94 { 95 *ptrfdm = fdm; 96 return pid; 97 } 98 }

转载地址:http://dcgdl.baihongyu.com/

你可能感兴趣的文章
解放智慧 智能家居对人到底是利还是弊
查看>>
安防物联网:海量分析技术仍是关键
查看>>
黄金法则:MySQL基准测试最佳实践
查看>>
Cisco reveals new initative
查看>>
Selenium2.0功能测试之forward与back
查看>>
微软扩大生态的又一步棋:推出 Visual Studio for Mac 预览版
查看>>
OA对于小微企业意味着什么?
查看>>
如何有效减少测试用例数目
查看>>
警惕!2016最大难题或源于云计算并发症
查看>>
亚信安全预警:一大波勒索软件变种来袭
查看>>
手机网民达7.8亿 移动网络安全不容忽视
查看>>
《算法基础》——3.4 有序链表
查看>>
《UNIX网络编程 卷2:进程间通信(第2版)》——2.3 创建与打开IPC通道
查看>>
商务直播跨海云:商务直播的那点事
查看>>
《MATLAB智能算法超级学习手册》一一第1章 MATLAB基础知识
查看>>
《Docker进阶与实战》——2.4节SparkContext概述
查看>>
《算法基础:打开算法之门》一导读
查看>>
《开源思索集》一成功的开源软件都有什么样的特点
查看>>
《Cisco IOS XR技术精要》一1.2 运营商级NOS需求
查看>>
Mozilla 拟在浏览器中增基于网页的虚拟现实功能
查看>>