为什么我可以使用/ dev?


41

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

zsh中此行为的图片。


@KamilMaciorowski,这是输出neofetch信息,您可以参考:) i.imgur.com/3azpnDt.png
haboutnnah

Answers:


43

历史上(直到V7 UNIX或1979年左右),read系统调用在文件和目录上均起作用。read在目录上返回一个简单的数据结构,用户程序将解析该结构以获取目录条目。确实,V7 ls工具确实做到了这一点- read在目录上,解析结果数据结构,并以结构化列表格式输出。

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

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

因为readdir现在是读取目录的默认方式,read(directory)所以通常在大多数现代OS上都未实现(返回-EISDIR)(例如,QNX是实现readdir为的显着异常read(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);

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

这实际上是一项功能,它是UNIX早期的一种保留,并且很长一段时间都没有被使用。我有些怀疑这是出于某些向后兼容性的原因而保留的,但是很容易有人会忽略该功能,因为它并没有真正损害任何功能。


可能是后者,因为它已从大多数目录中删除。
user253751 '18

36

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

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

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

某些BSD系统继续允许您打开所有目录以进行读取;我不知道它们是否从磁盘提供原始数据,或者是否返回模拟的Dirent列表,或者是否由文件系统驱动程序决定。

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

免责声明:我实际上尚未对BSD或macOS进行任何研究。我只是带翅膀。


这似乎是有道理的。存储的其他部分是否与标准OS文件系统不同?我以为/etc可以,所以我以此为基准。
haboutnnah

2
相反,通常/ etc只是包含常规文件的常规文件夹。但是,可能还有其他虚拟文件系统–运行mount/sbin/mount查看当前在哪里安装了什么文件系统。
grawity

你是对的!看到这个:i.imgur.com/pcVpo1o.png
haboutnnah

这真的很整洁@grawity,i.imgur.com/8QuR0FK.png
haboutnnah

6
@haboutnnah您的屏幕快照确认这/dev是使用devfs驱动程序的虚拟文件系统,而/etc属于/使用apfs驱动程序的文件系统的一部分。因此,cat读取其中一个而不读取另一个的原因是apfsdevfs驱动程序之间的差异。
kasperd
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.