《APUE》读书笔记-第十九章伪终端

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实现如下所示:

View Code

 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实现如下所示:

View Code

  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实现如下所示:

View Code

  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 }

 

 

时间: 2024-08-06 20:09:16

《APUE》读书笔记-第十九章伪终端的相关文章

《APUE》读书笔记—第十二章线程控制

本章介绍了一个进程中多个线程之间如何保持数据的似有性及进程的系统调用如何与线程进行交互. 1.线程限制:  Single Unix定义了一线线程操作的限制,和其他的限制一样,可以通过sysconf来查询.和其它的限制使用目的一样,为了应用程序的在不同操作 系统的可移植性. 一些限制:  PTHREAD_DESTRUCTOR_ITERATIONS: 销毁一个线程数据最大的尝试次数,可以通过_SC_THREAD_DESTRUCTOR_ITERATIONS作为sysconf的参数查询.  PTHREA

《APUE》读书笔记-第十四章高级I/O

1.非阻塞I/O 对低速设备的I/O操作可能会使进程永久阻塞,这类系统调用主要有如下情况: (1)如果数据并不存在,则读文件可能会使调用者永远阻塞(例如读管道.终端设备和网络设备). (2)如果数据不能立即被接受,则写这些同样的文件也会使调用者永远阻塞: (3)在某些条件发生之前,打开文件会被阻塞(例如以只写方式打开一个FIFO,那么在没有其他进程已用读方式打开该FIFO时): (4)对已经加上强制性锁的文件进行读.写: (5)某些ioctl操作: (6)某些进程间通信函数: 非阻塞I/O调用o

《APUE》读书笔记-第十五章进程间通信

进程间通信(IPC)是指能在两个进程间进行数据交换的机制.现代OS都对进程有保护机制,因此两个进程不能直接交换数据,必须通过一定机制来完成. IPC的机制的作用: (1)一个软件也能更容易跟第三方软件或内核进行配合的集成,或移植.如管道,在shell 下执行 ps –aux | grep bash. (2)简化软件结构, 可以把一个软件划分多个进程或线程,通过IPC,集成在一起工作.如消息队列. (3)让操作系统各个模块交换数据,包括内核与应用程序机制. (4)提供进程之间或同一进程之间多线程的

《APUE》读书笔记-第十六章网络IPC:套接字

通过网络套接字可以使得不同计算机上运行的进程相互通信. 1.创建套接字 #include <sys/socket.h> Int socket( int domain, int type, int protocol); 注意:AF_LOCAL域是AF_UNIX的别名,AF_UNSPEC域可以代表任何域. 2.套接字通信是双向的,禁止套接字上的输入/输出 #include < sys/socket.h> Int shutdown ( int sockfd, int how); 3.处理

MYSQL必知必会读书笔记第十和十一章之使用函数处理数据_Mysql

 mysql简介 MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管理. 拼接字段 存储在数据库表中的数据一般不是应用程序所需要的格式.我们需要直接从数据库中检索出转换.计算或格式化过的数据:而不是检索出数据,然后再在客户机应用程序或报告程序中重新格式化. 计算字段(字段 = 列,不过数据库列一般称为列,而字段通常用于计算字段中)并不实际存在于数据库表中,计算字段是运行时在select语句内创建的

Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验

Android群英传笔记--第十二章:Android5.X 新特性详解,Material Design UI的新体验 第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看 Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 这一章很多,但是很有趣,也是这书的最后一章知识点了,我现在还在考虑要不要写这个拼图和2048的案例,在此之前,我们先来玩玩Android5.X的新特性吧!

python 教程 第十九章、 图形界面编程

第十九章. 图形界面编程 import Tkinter top = Tkinter.Tk() hello = Tkinter.Label(top, text='Hello World!') hello.pack() quit = Tkinter.Button(top, text='QUIT',command=top.quit) quit.pack(fill=Tkinter.X, expand=1) Tkinter.mainloop()

第十九章——使用资源调控器管理资源(1)——使用SQLServer Management Studio 配置资源调控器

原文:第十九章--使用资源调控器管理资源(1)--使用SQLServer Management Studio 配置资源调控器 本系列包含: 1. 使用SQLServer Management Studio 配置资源调控器 2. 使用T-SQL配置资源调控器 3. 监控资源调控器   前言:       在前面的章节,提到过可以通过多种配置数据库服务器的方式来提高性能.如索引.统计信息.hints.物理设计和服务器配置等.       当你完成上面那些配置后,还依旧有少量存储过程.查询运行得很慢时

第十九章——使用资源调控器管理资源(3)——监控资源调控器

原文:第十九章--使用资源调控器管理资源(3)--监控资源调控器 前言:       在对每个应用程序配置了资源调控器之后,需要监控资源调控器.可能需要监控资源池的使用和多少个请求被分配到特定的资源池.也可能希望监控internal和default池的活动情况.        本文中,演示使用不同登录账号(AW_WebAppUser和AW_ReportAppUser),并监控CPU和内存资源的使用情况.   准备工作: 本文使用的部分脚本在本系列的第一篇中已经写出.这里不累赘.文中将通过不同账号