进一步阅读

进一步阅读

打开伪终端的主部分后

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()在打开的文件描述符上。从设备出现在devfspts/目录下的文件系统中。

因此,内核确保它们从一开始就拥有正确的权限和所有权,并且不会有任何机会让它们拥有过时的权限和所有权。因此,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,用于容器等,并且当存在时,同步(正确的)两个文件系统非常困难许多 devtmpfsdevpts文件系统。所以在较新的“新”devpts系统中的所有设备(多路复用器和从设备)都位于同一个文件系统中。为了向后兼容,默认情况下新ptmx节点不可访问,除非设置新的ptmxmode挂载选项。
    • 在里面甚至更新“新”文件系统中的设备文件devpts现在是主要的多路复用器,并且是由内核提供的试图模仿符号链接的垫片、绑定安装或普通的旧设备ptmxdevptsptmxdevtmpfs实际的符号链接到pts/ptmx.
  • 内核并不总是按照应有的方式设置所有权和权限grantpt()。设置错误的挂载选项( GIDgid以外的选项ttymode0620 以外的选项)会触发 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().

进一步阅读

相关内容