`

打开伪终端设备

阅读更多
    在伪终端概述一节中已对 PTY进行了初步的介绍。尽管 PTY 表现得就像物理终端设备一样,不过在打开 PTY 设备文件时,应用程序并不需要设置 O_TTY_INIT 标识(见不带缓冲的文件I/O之open)。各种平台打开 PTY 设备的方法有所不同,posix_openpt 函数提供了一种可移植的方法来打开下一个可用的伪终端主设备。
#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。
分享到:
评论

相关推荐

    伪终端实现GSM

    伪终端是一种特殊的设备文件,用于模拟一个真实的终端。在Linux系统中,PTY被广泛应用于远程登录、脚本执行、图形界面的无头模式等场景。PTY由一对主从设备文件组成,其中主设备文件通常由应用程序(如telnet或ssh...

    Linux终端tty设备驱动编程.

    - open()、close()函数:实现终端设备的打开(open)和关闭(close)操作,确保资源得到正确分配和释放。 - 数据读写流程:实现数据的读取和写入流程,包括对终端设备的缓冲区操作和数据传输过程。 - tty设备线路...

    UNIX环境高级编程(18到23章)

    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 ...

    pts:具有创建伪终端字符设备的经验

    1. **打开伪终端**:使用`openpty()`函数或者通过一系列的`open()`、`fcntl()`和`ioctl()`调用来获取主从两端的文件描述符。 2. ** fork子进程**:使用`fork()`创建一个新的进程。父进程继续处理主端,子进程则会...

    Unix环境高级编程19

    在图9-5所示的场景中,伪终端设备驱动程序扮演着桥梁的角色,连接网络登录和shell程序,实现对终端特性的模拟。 19.2 概述伪终端的概念,指出它不是一个真实的终端设备,而是一个软件实现的接口,让进程能够像操作...

    UNIX环境高级编程_第2版.part1

    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 小结...

    UNIX环境高级编程_第2版.part2

    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 小结...

    UNIX环境高级编程_第二版中文

    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 小结  习题 ...

    UNIX环境高级编程(第二版中文)

    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 高级特性...

    unix环境编程电子书

    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 高级...

    UNIX环境高级编程部分

    ### 关键知识点提取 #### 第1章:UNIX基础知识 - **登录**:用户通过特定的...- **打开伪终端设备**:创建和使用伪终端的方法。 - **pty_fork函数**:创建伪终端的辅助函数。 - **pty程序**:使用伪终端的示例程序。...

    UNIX高级编程 计算机科学丛书

    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 运行协同进程...

    UNIX环境高级编程 pdf格式

    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 ...

    UNIX环境高级编程(PDF)

    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 ...

Global site tag (gtag.js) - Google Analytics