为什么我可以cat /dev?

hab*_*nah 42 cat macos

我可以cat /dev,我可以ls /dev,我不能less /dev。为什么cat让我cat这个目录而不是其他目录?

zsh 中此行为的图片。

nne*_*neo 44

历史上(直到 V7 UNIX,或大约 1979 年)read系统调用对文件和目录都有效。read在目录上将返回一个简单的数据结构,用户程序将解析该数据结构以获得目录条目。事实上,V7ls工具正是这样做的——read在一个目录上,解析结果数据结构,以结构化列表格式输出。

随着文件系统变得越来越复杂,这个“简单”的数据结构变得越来越复杂,以至于readdir添加了一个库函数来帮助程序解析read(directory). 不同的系统和文件系统可能有不同的磁盘格式,这变得越来越复杂。

当 Sun 推出网络文件系统 (NFS) 时,他们希望完全抽象出磁盘上的目录结构。然而,他们并没有让他们的read(directory)返回成为一个独立于平台的目录表示,而是添加了一个新的系统调用getdirents——并禁止read在网络安装目录上。这个系统调用很快适用于各种 UNIX 风格的所有目录,使其成为获取目录内容的默认方式。(历史摘自https://utcc.utoronto.ca/~cks/space/blog/unix/ReaddirHistory

因为readdir现在是读取目录的默认方式,read(directory)在大多数现代操作系统上通常不会实现(返回 -EISDIR)(例如,QNX 是一个显着的例外,它实现readdirread(directory))。然而,对于大多数现代内核中的“虚拟文件系统”设计,读取目录是否有效实际上取决于单个文件系统。

事实上,在 macOS 上,挂载点devfs底层的文件系统/dev确实支持读取(https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/miscfs/devfs/devfs_vnops.c#L629) :

static int
devfs_read(struct vnop_read_args *ap)
{
        devnode_t * dn_p = VTODN(ap->a_vp);

    switch (ap->a_vp->v_type) {
      case VDIR: {
          dn_p->dn_access = 1;

          return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context);
Run Code Online (Sandbox Code Playgroud)

READDIR如果您尝试读取/dev(读取下的文件/dev由单独的函数 - 处理),则显式调用devfsspec_read。因此,如果程序调用read系统调用/dev,它将成功并获得目录列表!

这实际上是 UNIX 早期遗留下来的一个特性,并且已经很长时间没有被触及了。我的一部分怀疑这是出于某些向后兼容性原因而保留的,但也可能是这样一个事实,即没有人足够关心删除该功能,因为它并没有真正伤害任何东西。


use*_*686 36

Less是一个文本文件查看器,cat是一个复制任意数据的工具。因此,less会执行自己的检查,以确保您不会打开包含大量数据或行为异常的内容。另一方面,cat根本没有这样的检查——如果内核允许你打开某些东西(即使它是管道或设备或更糟的东西),cat会读取它。

那么为什么操作系统允许cat打开目录呢?传统上,在BSD风格的系统的所有目录都可以作为文件读取,这就是程序首先列出目录的方式:仅通过解释存储在磁盘上的目录结构。

后来,这些磁盘结构开始与内核使用的目录不同:以前目录是线性列表,后来文件系统开始使用哈希表、B 树等。所以直接读取目录不再简单——内核为此开发了专用功能。(我不确定这是否是主要原因,或者它们是否主要是出于其他原因(例如缓存)而添加的。)

一些 BSD 系统继续让你打开所有目录进行阅读;我不知道他们是从磁盘给你原始数据,还是他们返回一个模拟的目录,或者他们是否让文件系统驱动程序决定。

因此,macOS 可能是内核允许的操作系统之一,只要文件系统提供数据即可。不同之处在于,在早期编写/devdevfs文件系统上允许这样做,而/在 APFS 文件系统上则省略了此功能,因为在现代是不必要的。

免责声明:我实际上并没有对 BSD 或 macOS 进行任何研究。我只是在展翅高飞。

  • @haboutnnah 您的屏幕截图确认`/dev` 是使用`devfs` 驱动程序的虚拟文件系统,而`/etc` 是使用`apfs` 驱动程序的`/` 文件系统的一部分。所以 `cat` 会读取一个而不是另一个的原因是 `apfs` 和 `devfs` 驱动程序之间的差异。 (6认同)
  • 相反,通常 /etc 只是一个包含常规文件的常规文件夹。_可能_还有其他虚拟文件系统 - 运行 `mount` 或 `/sbin/mount` 以查看当前安装的位置。 (2认同)