- 浏览: 142343 次
文章分类
最新评论
在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 PTY 表现得就像物理终端设备一样,不过在打开 PTY 设备文件时,应用程序并不需要设置 O_TTY_INIT 标识(见不带缓冲的文件I/O之open)。各种平台打开 PTY 设备的方法有所不同,posix_openpt 函数提供了一种可移植的方法来打开下一个可用的伪终端主设备。
参数 oflag 是一个位屏蔽字,指定如何打开主设备。它类似于 open 函数的 oflag 参数,但只支持指定 O_RDWR 来打开主设备进行读、写,或指定 O_NOCTTY 来防止主设备成为调用者的控制终端,其他打开标志都会导致未定义的行为。
在伪终端从设备可用之前,它的权限必须设置,以便应用程序可以访问它。grantpt 函数可以把从设备节点的用户 ID 设置为调用者的实际用户 ID,设置其组 ID 为一非指定值,通常是可以访问该终端设备的组。权限被设置为 0620,即对个体所有者是读/写,对组所有者是写。实现通常将 PTY 从设备的组所有者设置为 tty 组。把那些要对系统中所有活动端具有写权限的程序(如 wall(1)和 write(1))的设置组 ID 设置为 tty 组,因为在 PTY 从设备上 tty 组的写权限是被允许的。
这几个函数中的 fd 参数都是与伪终端主设备关联的文件描述符。
为了更改从设备节点的权限,grantpt 可能需要 fork 并 exec 一个设置用户 ID 程序(如 Solaris 中是 /usr/lib/pt_chmod),因此调用者可能捕捉到行为未定义的 SIGCHLD 信号。
unlockpt 函数用于准予对 PTY 从设备的访问,从而允许应用程序打开该设备。阻止其他进程打开从设备后,建立该设备的应用程序有机会在使用主、从设备之前正确地初始化这些设备。
若给定了伪终端主设备的文件描述符,则可以用 ptsname 函数找到伪终端从设备的路径名(该名字可能存储在静态存储中,因此后续的调用可能会覆盖它)。
为了方便处理相关细节,下面自定义了两个函数:ptym_open 和 ptys_open。ptym_open 打开下一个可用的 PTY 主设备。调用者必须分配一个数组来存放主设备或从设备的名字,并且如果调用成功,相应的从设备名会通过 pts_name 返回。然后将这个名字传给用来打开该从设备的 ptys_open 函数。缓冲区的字节长度由 pts_namesz 传送,使得 ptym_open 不会复制比该缓冲区长的字符串。不过一般不直接调用这两个函数,而是由另一个自定义的函数 pty_fork 来调用,并且还会 fork 出一个子进程。一个进程调用 ptym_open 来打开一个主设备并得到从设备名,然后 fork 子进程。子进程在调用 setsid 建立新的会话后调用 ptys_open 打开从设备。这就是从设备如何成为子进程控制终端的过程。
在 pty_fork 函数中,PTY 主设备的文件描述符通过参数 ptrfdm 指针返回。如果参数 slave_name 不为空,则从设备名被存储在该指针指向的由调用者分配的存储区中。如果 slave_termios 不为空,则系统使用该指针引用的结构初始化从设备的终端行规程,否则系统会把从设备的 termios 结构设置成实现定义的初始状态。类似地,如果 slave_winsize 指针不为空,则按该指针引用的结构初始化从设备的窗口大小,否则 winsize 结构(见终端窗口大小和 termcap)一般初始化为 0。我们还把从设备的文件描述符复制给了子进程的标准输入、标准输出和标准错误。这意味着以后不管子进程 exec 何种程序,它都具有同 PTY 从设备联系起来的这 3 个描述符。如果该函数成功执行,它会在子进程中返回 0,在父进程中返回子进程的进程 ID,否则返回 -1。
#include <stdlib.h> #include <fcntl.h> int posix_openpt(int oflag); /* 返回值:若成功,返回下一个可用的 PTY 主设备的文件描述符;否则,返回 -1 */
参数 oflag 是一个位屏蔽字,指定如何打开主设备。它类似于 open 函数的 oflag 参数,但只支持指定 O_RDWR 来打开主设备进行读、写,或指定 O_NOCTTY 来防止主设备成为调用者的控制终端,其他打开标志都会导致未定义的行为。
在伪终端从设备可用之前,它的权限必须设置,以便应用程序可以访问它。grantpt 函数可以把从设备节点的用户 ID 设置为调用者的实际用户 ID,设置其组 ID 为一非指定值,通常是可以访问该终端设备的组。权限被设置为 0620,即对个体所有者是读/写,对组所有者是写。实现通常将 PTY 从设备的组所有者设置为 tty 组。把那些要对系统中所有活动端具有写权限的程序(如 wall(1)和 write(1))的设置组 ID 设置为 tty 组,因为在 PTY 从设备上 tty 组的写权限是被允许的。
#include <stdlib.h> int grantpt(int fd); int unlockpt(int fd); /* 两个函数的返回值:若成功,返回 0;否则,返回 -1 */ char *ptsname(int fd); /* 返回值:若成功,返回指向 PTY 从设备名的指针;否则,返回 NULL */
这几个函数中的 fd 参数都是与伪终端主设备关联的文件描述符。
为了更改从设备节点的权限,grantpt 可能需要 fork 并 exec 一个设置用户 ID 程序(如 Solaris 中是 /usr/lib/pt_chmod),因此调用者可能捕捉到行为未定义的 SIGCHLD 信号。
unlockpt 函数用于准予对 PTY 从设备的访问,从而允许应用程序打开该设备。阻止其他进程打开从设备后,建立该设备的应用程序有机会在使用主、从设备之前正确地初始化这些设备。
若给定了伪终端主设备的文件描述符,则可以用 ptsname 函数找到伪终端从设备的路径名(该名字可能存储在静态存储中,因此后续的调用可能会覆盖它)。
为了方便处理相关细节,下面自定义了两个函数:ptym_open 和 ptys_open。ptym_open 打开下一个可用的 PTY 主设备。调用者必须分配一个数组来存放主设备或从设备的名字,并且如果调用成功,相应的从设备名会通过 pts_name 返回。然后将这个名字传给用来打开该从设备的 ptys_open 函数。缓冲区的字节长度由 pts_namesz 传送,使得 ptym_open 不会复制比该缓冲区长的字符串。不过一般不直接调用这两个函数,而是由另一个自定义的函数 pty_fork 来调用,并且还会 fork 出一个子进程。一个进程调用 ptym_open 来打开一个主设备并得到从设备名,然后 fork 子进程。子进程在调用 setsid 建立新的会话后调用 ptys_open 打开从设备。这就是从设备如何成为子进程控制终端的过程。
#include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <termios.h> #include <sys/ioctl.h> int ptym_open(char *pts_name, int pts_namesz){ int fdm = posix_openpt(O_RDWR); if(fdm < 0) return -1; if(grantpt(fdm) < 0) // grant access to slave goto errout; if(unlockpt(fdm) < 0) // clear slave's lock flag goto errout; char *ptr = ptsname(fdm); // get slave's name if(ptr == NULL) goto errout; strncpy(pts_name, ptr, pts_namesz); // save name of slave pts_name[pts_namesz - 1] = '\0'; return fdm; errout: int err = errno; close(fdm); errno = err; return -1; } int ptys_open(char *pts_name){ return open(pts_name, O_RDWR); } pid_t pty_fork(int *ptyfdm, char *slave_name, int slave_namesz, const struct termios *slave_termios, const struct winsize *slave_winsize){ char pts_name[20]; int fdm = ptym_open(pts_name, sizeof(pts_name)); if(fdm < 0){ printf("can't open master pty: %s, error %d\n", pts_name, fdm); exit(1); } if(slave_name != NULL){ strncpy(slave_name, pts_name, slave_namesz); slave_name[slave_namesz-1] = '\0'; } pid_t pid = fork(); if(pid < 0) return -1; if(pid > 0){ // parent *ptyfdm = fdm; // save fd of master return pid; // parent return pid of child } close(fdm); // all done with master in child if(setsid() < 0){ printf("setsid error\n"); exit(1); } /* Linux/Solaris acquires controlling terminal on open(). */ int fds = ptys_open(pts_name); if(fds < 0){ printf("can't open slave pty\n"); exit(1); } #if defined(BSD) /* TIOCSCTTY is the BSD way to acquire a controlling terminal. */ if(ioctl(fds, TIOCSCTTY, (char *)0) < 0){ printf("TIOCSCTTY error\n"); exit(1); } #endif if(slave_termios != NULL) tcsetattr(fds, TCSANOW, slave_termios); if(slave_winsize != NULL) ioctl(fds, TIOCSWINSZ, slave_winsize); /* Slave becomes stdin/stdout/stderr of child. */ dup2(fds, STDIN_FILENO); dup2(fds, STDOUT_FILENO); dup2(fds, STDERR_FILENO); if(fds!=STDIN_FILENO && fds!=STDOUT_FILENO && fds!=STDERR_FILENO){ printf("dup2 error to stdin/stdout/stderr\n"); close(fds); } return 0; // child returns 0 just like fork() }
在 pty_fork 函数中,PTY 主设备的文件描述符通过参数 ptrfdm 指针返回。如果参数 slave_name 不为空,则从设备名被存储在该指针指向的由调用者分配的存储区中。如果 slave_termios 不为空,则系统使用该指针引用的结构初始化从设备的终端行规程,否则系统会把从设备的 termios 结构设置成实现定义的初始状态。类似地,如果 slave_winsize 指针不为空,则按该指针引用的结构初始化从设备的窗口大小,否则 winsize 结构(见终端窗口大小和 termcap)一般初始化为 0。我们还把从设备的文件描述符复制给了子进程的标准输入、标准输出和标准错误。这意味着以后不管子进程 exec 何种程序,它都具有同 PTY 从设备联系起来的这 3 个描述符。如果该函数成功执行,它会在子进程中返回 0,在父进程中返回子进程的进程 ID,否则返回 -1。
发表评论
-
伪终端概述
2018-06-02 11:05 1548伪终端就是指,一个应用程序看上去像一个终端,但事实上它 ... -
终端窗口大小和 termcap
2018-05-29 22:39 799多数 UNIX 系统都提供了一种跟踪当前终端窗口大小的 ... -
终端规范模式和非规范模式
2018-05-29 00:25 948终端规范模式很简单:发一个读请求,当一行已经输入后,终 ... -
终端标识
2018-05-23 11:18 568尽管控制终端的名字在多数 UNIX 系统上都是 /de ... -
波特率和行控制函数
2018-05-22 07:53 943虽然大多数终端设 ... -
终端属性和选项标志
2018-05-20 07:40 709tcgetattr 和 tcsetattr ... -
终端特殊输入字符
2018-05-17 06:33 814终端支持下表所示的特殊输入字符。 为了更改 ... -
终端 I/O 综述
2018-05-10 07:56 437终端设备可认为是由内核中的终端驱动程序控制的。每个终端 ... -
POSIX 信号量
2018-05-09 00:03 579在XSI IPC通信之信 ... -
XSI IPC 通信之共享存储
2018-04-25 07:18 946在XSI IPC通信之消息队列和XSI IPC通信之信 ... -
XSI IPC通信之信号量
2018-04-17 23:38 616在XSI IPC通信之消 ... -
XSI IPC通信之消息队列
2018-04-15 10:54 497消息队列是消息的链接表,存储在内核中,由消息队列标识符 ... -
XSI IPC 相似特征介绍
2018-02-08 23:48 484有 3 种称作 XSI IPC ... -
IPC 通信之 FIFO
2018-02-06 22:55 421FIFO 也被称为命名管道,未命名的管道只能在两个相关 ... -
IPC 通信之管道
2018-01-30 22:22 389管道是 UNIX 系统 IPC 的最古老但也是最常用的 ... -
readv/writev 函数及存储映射 I/O
2018-01-19 00:57 889readv 和 writev 函数可用于在一次函数调用 ... -
POSIX 异步 I/O
2018-01-16 21:33 454POSIX 异步 I/O 接口为对不同类型的文件进行异 ... -
fcntl 记录锁
2018-01-06 23:48 615记录锁的功能是:当有进程正在读或修改文件的某个部分时, ... -
守护进程惯例
2018-01-06 23:52 439UNIX 系统中,守护进程遵循下列通用惯例。 ... -
守护进程编写规则与出错记录
2017-12-26 01:53 454在编写守护进程程 ...
相关推荐
伪终端是一种特殊的设备文件,用于模拟一个真实的终端。在Linux系统中,PTY被广泛应用于远程登录、脚本执行、图形界面的无头模式等场景。PTY由一对主从设备文件组成,其中主设备文件通常由应用程序(如telnet或ssh...
- open()、close()函数:实现终端设备的打开(open)和关闭(close)操作,确保资源得到正确分配和释放。 - 数据读写流程:实现数据的读取和写入流程,包括对终端设备的缓冲区操作和数据传输过程。 - tty设备线路...
19.3 打开伪终端设备 480 19.3.1 SVR4 481 19.3.2 4.3+BSD 482 19.4 pty_fork函数 484 19.5 pty程序 486 19.6 使用pty程序 489 19.6.1 utmp文件 489 19.6.2 作业控制交互 489 19.6.3 检查长时间运行程序的输出 491 ...
1. **打开伪终端**:使用`openpty()`函数或者通过一系列的`open()`、`fcntl()`和`ioctl()`调用来获取主从两端的文件描述符。 2. ** fork子进程**:使用`fork()`创建一个新的进程。父进程继续处理主端,子进程则会...
在图9-5所示的场景中,伪终端设备驱动程序扮演着桥梁的角色,连接网络登录和shell程序,实现对终端特性的模拟。 19.2 概述伪终端的概念,指出它不是一个真实的终端设备,而是一个软件实现的接口,让进程能够像操作...
19.3 打开伪终端设备544 19.3.1 基于streams的伪终端547 19.3.2 基于bsd的伪终端549 19.3.3 基于linux的伪终端551 19.4 pty_fork函数553 19.5 pty程序555 19.6 使用pty程序559 19.7 高级特性564 19.8 小结...
19.3 打开伪终端设备544 19.3.1 基于streams的伪终端547 19.3.2 基于bsd的伪终端549 19.3.3 基于linux的伪终端551 19.4 pty_fork函数553 19.5 pty程序555 19.6 使用pty程序559 19.7 高级特性564 19.8 小结...
19.3 打开伪终端设备 19.3.1 基于STREAMS的伪终端 19.3.2 基于BSD的伪终端 19.3.3 基于Linux的伪终端 19.4 pty_fork函数 19.5 pty程序 19.6 使用pty程序 19.7 高级特性 19.8 小结 习题 ...
19.3 打开伪终端设备 544 19.3.1 基于STREAMS的伪终端 547 19.3.2 基于BSD的伪终端 549 19.3.3 基于Linux的伪终端 551 19.4 pty_fork函数 553 19.5 pty程序 555 19.6 使用pty程序 559 19.7 高级特性...
541 19.3 打开伪终端设备 544 19.3.1 基于STREAMS的伪终端 547 19.3.2 基于BSD的伪终端 549 19.3.3 基于Linux的伪终端 551 19.4 pty_fork函数 553 19.5 pty程序 555 19.6 使用pty程序 559 19.7 高级...
### 关键知识点提取 #### 第1章:UNIX基础知识 - **登录**:用户通过特定的...- **打开伪终端设备**:创建和使用伪终端的方法。 - **pty_fork函数**:创建伪终端的辅助函数。 - **pty程序**:使用伪终端的示例程序。...
19.3 打开伪终端设备 19.3.1 SVR4 19.3.2 4.3+BSD 19.4 pty_fork函数 19.5 pty程序 19.6 使用pty程序 19.6.1 utmp文件 19.6.2 作业控制交互 19.6.3 检查长时间运行程序的输出 19.6.4 script程序 19.6.5 运行协同进程...
19.3 打开伪终端设备 480 19.3.1 SVR4 481 19.3.2 4.3+BSD 482 19.4 pty_fork函数 484 19.5 pty程序 486 19.6 使用pty程序 489 19.7 其他特性 494 19.8 小结 495 习题 495 附录A 函数原型 497 附录B 其他源代码 512 ...
19.3 打开伪终端设备 480 19.3.1 SVR4 481 19.3.2 4.3+BSD 482 19.4 pty_fork函数 484 19.5 pty程序 486 19.6 使用pty程序 489 19.6.1 utmp文件 489 19.6.2 作业控制交互 489 19.6.3 检查长时间运行程序的输出 491 ...