打开伪终端的主部分后
int fd_pseudo_term_master = open("/dev/ptmx",O_RDWR);
创建了一个文件/dev/pts/[NUMBER]
,代表slave
伪终端的一部分。
像我这样无知的人可能会想象,完成后ptsname(fd_pseudo_term_master,filename_pseudo_term_slave,buflen);
应该设置为简单地
做事int fd_pseudo_term_slave = open(filename_pseudo_term_slave,O_RDWR);
并做好事。
然而,必须有一个非常重要的“锁定”伪终端从机的用例,因为为了使事情变得简单,在open
,有必要使用男人3解锁,“解锁”。
我无法找出这个用例是什么?伪终端初始锁定的必要性是什么?代码实现了什么(取自 libc)
/* Unlock the slave pseudo terminal associated with the master pseudo
terminal specified by FD. */
int
unlockpt (int fd)
{
#ifdef TIOCSPTLCK
int save_errno = errno;
int unlock = 0;
if (ioctl (fd, TIOCSPTLCK, &unlock))
{
if (errno == EINVAL)
{
errno = save_errno;
return 0;
}
else
return -1;
}
#endif
/* If we have no TIOCSPTLCK ioctl, all slave pseudo terminals are
unlocked by default. */
return 0;
}
如果可能的话,答案将详细说明历史或当前的用例。
问题的额外部分是:
当前的 Linux 内核是否仍然依赖“锁定伪终端从机”的功能?
想法:这是避免赛车冲突的低效尝试吗?
在等待答案时,我更多地研究了 linux 内核源代码,但自己没有任何好的答案。然而,似乎可以从伪终端的初始锁定情况中“提取”的一种用途是为伪终端主进程提供一些时间来设置对文件的某些访问权限/dev/pts/[NUMBER]
,以防止某些用户首先访问该文件。这可以作为答案的一部分吗?然而奇怪的是,这种“初始锁定”状态似乎并不能真正阻止从属文件的多次打开,至少在我看来是保证原子性的。
答案1
旧的 AT&T System 5 伪终端从设备机制是它们是普通的执着的下的字符设备节点/dev
。有一个多路复用器掌握设备位于/dev/ptmx
.旧的 4.3BSD 伪终端设备机制具有并行的普通终端设备对执着的下的主从设备节点/dev
。
在这两种情况下,这意味着从设备文件在上次文件描述符关闭后保留了其最后的所有权和权限。因此grantpt()
,在(重新)分配(重新)分配(重新使用)伪终端后,修复从属设备文件的所有权和权限的功能不断发展。
open()
这反过来意味着,当程序在和之间设置重复使用的伪终端时,存在一个窗口,grantpt()
事先拥有从属设备的人也可以潜入并打开它,从而有可能访问其他人的终端。因此,伪终端从属字符设备的想法是从锁定状态开始,在锁定状态下它们无法打开,并unlockpt()
在grantpt()
成功执行后解锁。
多年来,事实证明这是不必要的。
如今,从设备文件不是持久性的,因为内核自己创建和销毁东西/dev
。打开主设备的行为要么重置从设备权限和所有权,要么彻底重新创建从设备文件(在后一种情况下,当所有打开的文件描述符关闭时,从设备文件再次消失),无论哪种情况都以原子方式进行相同的系统调用。
PTMGET
在 OpenBSD 上,这是设备上 I/O 控制功能的一部分/dev/ptm
。/dev
仍然是一个磁盘卷,内核内部发出相关调用以在那里创建新的设备节点并重置其所有权和权限。- 在 FreeBSD 上,这是通过
posix_openpt()
系统调用完成的。/dev
根本不是光盘卷。它是一个devfs
文件系统。它不包含“多路复用器”设备也不包含主设备文件,因为它posix_openpt()
是一个彻底的系统调用,而不是包装ioctl()
在打开的文件描述符上。从设备出现在devfs
其pts/
目录下的文件系统中。
因此,内核确保它们从一开始就拥有正确的权限和所有权,并且不会有任何机会让它们拥有过时的权限和所有权。因此,grantpt()
和unlockpt()
库函数本质上是无操作的,其唯一剩余的功能是检查它们传递的文件描述符并设置它EINVAL
是否不是伪终端的主端,因为程序可能会做一些愚蠢的事情,例如传递非伪终端- 这些函数的终端文件描述符并期望它们返回错误。
有一段时间,在 Linux 上,伪终端从设备是持久设备节点。 GNU C 库grantpt()
不是系统调用。相反,它分叉并执行了一个名为 的 set-UID 帮助程序pt_chown
,这让没有设置 UID 可执行文件人群。 (grantpt()
记住,必须允许非特权用户更改其不一定拥有的特殊设备文件的所有权和权限。)因此仍然存在机会之窗,并且 Linux 仍然必须维护unlockpt()
.
它的“新”devpts
文件系统(其中“新”意味着几年前引入,现在引入)几乎然而,允许以与 FreeBSD 相同的方式执行操作devfs
。有一些差异。
- 还有一个“多路复用器”设备。
- 在里面较老的“新”
devpts
系统,这是ptmx
不同devtmpfs
文件系统中的设备,该devpts
文件系统仅包含自动创建/销毁的从设备文件。按照惯例,该设置是在 处/dev/ptmx
附带一个安装座。devpts
/dev/pts
- 但 Linux 人们想要拥有多个完全独立的文件系统的实例
devpts
,用于容器等,并且当存在时,同步(正确的)两个文件系统非常困难许多devtmpfs
和devpts
文件系统。所以在较新的“新”devpts
系统中的所有设备(多路复用器和从设备)都位于同一个文件系统中。为了向后兼容,默认情况下新ptmx
节点不可访问,除非设置新的ptmxmode
挂载选项。 - 在里面甚至更新“新”文件系统中的设备文件
devpts
现在是主要的多路复用器,并且是由内核提供的试图模仿符号链接的垫片、绑定安装或普通的旧设备ptmx
devpts
ptmx
devtmpfs
实际的符号链接到pts/ptmx
.
- 在里面较老的“新”
- 内核并不总是按照应有的方式设置所有权和权限
grantpt()
。设置错误的挂载选项( GIDgid
以外的选项tty
或mode
0620 以外的选项)会触发 GNU C 库中的回退行为。为了grantpt()
根据需要减少 GNU C 库中的无操作,内核必须不是分配打开进程的组(即必须有明确的gid
设置),分配的组必须是tty
组,并且mode
新创建的从设备的组必须正好是0620。
默认情况下不打开/dev/pts/ptmx
以及 GNU C 库未完全简化grantpt()
为无操作都是因为内核和 C 库没有保持同步。每个人都必须使用另一个人的旧版本进行操作。 Linux 仍然必须提供较旧的/dev/ptmx
.pt_chown
如果没有devpts
具有正确安装选项的新文件系统,GNU C 库仍然必须回退到运行状态。
因此,unlockpt()
如果devpts
挂载选项错误,GNU C 库必须回退到实际在grantpt()
.
进一步阅读
- https://unix.stackexchange.com/a/470853/5132
- 解决这个 glibc 问题的最佳方法是什么?
- https://unix.stackexchange.com/a/214685/5132
Documentation/filesystems/devpts.txt
。 Linux 内核。- 丹尼尔·贝兰奇 (2009-05-20)。
/dev/pts
必须使用“newinstance”挂载标志以避免容器出现安全问题。红帽错误#501718。 - 乔纳森·德博因·波拉德 (2018)。
open-controlling-tty
。小吃指南。软件。 - 乔纳森·德博因·波拉德 (2018)。
vc-get-tty
。小吃指南。软件。 - 乔纳森·德博因·波拉德 (2018)。
pty-get-tty
。小吃指南。软件。