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 <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <fcntl.h>
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 这个命令来查看当前所使用的终端名。参考自http://blog.csdn.net/heron804/article/details/8144103

介绍另外两个函数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 <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <stropts.h>
7 #include <string.h>
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 <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <stropts.h>
7 #include <string.h>
8 #include <errno.h>
9 #include<grp.h>
10 #include <sys/types.h>
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 <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <stropts.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <asm/ioctl.h>
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 <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <stropts.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <asm/ioctl.h>
10 #include <termios.h>
11 #ifndef IIOCGWINSZ
12 #include <sys/ioctl.h>
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 }

Unix环境高级编程(二十)伪终端的更多相关文章

  1. Unix环境高级编程(二)文件和目录

    本章主要介绍的是文件结构及目录.重点是通过stat函数获取文件的结构信息,然后是文件目录及其遍历.学完本章后,编写了一个输出给的目录下的文件信息的程序. 首先是包含在<sys/stat.h> ...

  2. Unix环境高级编程(二十一)数据库函数库

    本章的内容是开发一个简单的.多用户数据库的C函数库.调用此函数库提供的C语言函数,其他程序可以读取和存储数据库中的记录.绝大部分商用数据库函数库提供多进程同时更新数据库所需要的并发控制,采用建议记录锁 ...

  3. (十二) 一起学 Unix 环境高级编程 (APUE) 之 进程间通信(IPC)

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  4. (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  5. (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  6. [置顶] 文件和目录(二)--unix环境高级编程读书笔记

    在linux中,文件的相关信息都记录在stat这个结构体中,文件长度是记录在stat的st_size成员中.对于普通文件,其长度可以为0,目录的长度一般为1024的倍数,这与linux文件系统中blo ...

  7. Unix 环境高级编程

    UNIX 环境高级编程 本书描述了UNIX系统的程序设计接口--系统调用接口和标准C库提供的很多函数. 与大多数操作系统一样,Unix为程序员运行提供了大量的服务--打开文件,读文件,启动一个新程序, ...

  8. 《UNIX环境高级编程》(APUE) 笔记系列

    本系列笔记主要是对于 <UNIX环境高级编程>(APUE) 各章节内容 概念性的总结 ,不涉及代码解读 . 目录 : 第一章 UNIX基础知识 第二章 UNIX标准及实现 第三章 文件I/ ...

  9. (六) 一起学 Unix 环境高级编程 (APUE) 之 进程控制

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

随机推荐

  1. 只用CSS做到完全居中

    我们都知道 margin:0 auto; 的样式能让元素水平居中,而 margin: auto; 却不能做到垂直居中……直到现在.但是,请注意!想让元素绝对居中,只需要声明元素高度,并且附加以下样式, ...

  2. Android之WifiManager

    移动设备离不开网络,android平台中在包android.net.wifi下提供了一些类专门用于管理设备的Wifi功能.该包下主要存在如下几个类: 1.  ScanResult:主要用来描述通过Wi ...

  3. Metronic V1.5.2 Responsive Admin Dashboard Template build with Twitter Bootstrap 3.0

    Template Name: Metronic - Responsive Admin Dashboard Template build with Twitter Bootstrap 3.0 Versi ...

  4. Postfix接收邮件后转向运行特定的脚本

    本文主要參考:http://serverfault.com/questions/258469/how-to-configure-postfix-to-pipe-all-incoming-email-t ...

  5. 【AngularJS】Controller

    理解控制器 在Angular中,一个容器就是一个JavaScript构造函数,用来增强Angular Scope. 当一个控制器通过ng-controller指令绑定到DOM,Angular就会实例化 ...

  6. Appium Python 四:怎样获取APP的Package以及Activity

    看到一篇很好的博客:[Android测试][随笔]获得App的包名和启动页Activity 除了博客上的方法,我还找到两种方法: 方法一:aapt 前提需要使用SDK Manager.exe 下载 A ...

  7. ubuntu server 安装 question2answer 及 汉化包

    ubuntu server 安装 question2answer 及 汉化包 question2answer 是一个非常简洁方便的问答系统,可以用它快速的部署一个问答社区,提高在开发中的交流沟通效率: ...

  8. html5 canvas类库 实例

    http://threejs.org/examples/ http://ocanvas.org/demos https://processing.org/examples/rotatexy.html ...

  9. Sql_Handle and Plan_Handle Explained

    For batches, the SQL handles are hash values based on the SQL text. For database objects such as sto ...

  10. js setInterval() 用法示例

      Created by Marydon on 1.定义 语法:setInterval(param1,param2) param1 要调用的函数或要执行的代码串. param2 周期性调用param1 ...