为什么 ls 命令中的当前目录被标识为与自身链接?

为什么 ls 命令中的当前目录被标识为与自身链接?

在《学习UNIX操作系统》一书中,有一节“3.1.8 列出文件”描述了该ls命令。

在段落中ls -l描述了该命令输出的列。

该命令的第二列ls -l包含一个数字。该数字在书中被描述为“链接到此文件和目录的数量”。(链接到与相关数字同一行的最后一列中命名的文件或目录。)

我尝试了这个命令,并将输出与当前目录中文件和目录的实际数量进行了比较。

ls -l
drwxr-xr-x   6 azbc  staff    192 Sep  7 16:09 test

在目录中test,我有 2 个子目录和 1 个文件,以及 1 个隐藏文件和当前目录的列表,再加上父目录的列表,因此共有 6 个文件和目录。

 ls -a -F
 ./                .hidden_file.txt  dir_2/
 ../               dir_1/            file_1.sh

我觉得将所有文件和目录(包括隐藏文件和目录)标识为链接到当前目录是合乎逻辑的。将父目录标识为链接到当前目录也是合乎逻辑的。

但是为什么当前目录被标识为与自身链接?

测试目录的命令ls -la给出以下输出。(/如果是目录,则 -F 选项在目录名后面显示 ,如果是可执行文件,则显示 *)

 ls -la -F
 total 0
 drwxr-xr-x   6 azbc  staff   192 Sep  7 16:09 ./
 drwxr-xr-x+ ?? azbc  staff    ?? Sep  7 16:06 ../
 -rw-r--r--   1 azbc  staff     0 Sep  7 16:09 .hidden_file.txt
 drwxr-xr-x   2 azbc  staff    64 Sep  7 16:06 dir_1/
 drwxr-xr-x   2 azbc  staff    64 Sep  7 16:06 dir_2/
 -rwx--x--x   1 azbc  staff     0 Sep  7 16:06 file_1.sh*

文件本身仅用一个链接标识。文件是否链接到自身?还是链接到其所在的目录?

由于目录列表中目录本身在列表中表示,因此在逻辑上可以算作链接。

然而,在文件本身的列表中,只有文件本身出现在列表中。

 ls -la -F file_1.sh
 -rwx--x--x  1 azbc  staff  0 Sep  7 16:06 file_1.sh

由此可以合理地说该文件与其自身相链接。

然而,对我来说,说文件链接到它所在的目录似乎更合乎逻辑。

这似乎不是结果。

或者,链接文件的列表仅仅是对命令列表输出中存在的文件和目录的计数,而不是对文件系统中文件或目录的真实链接的识别?

编辑:作为回复@George Udosen, 在:

“现在尝试回答您在评论中提出的问题:

“这里列出的链接是什么?列出的是文件本身吗?还是列出的是包含该文件的目录?”

如果我列出目录test

 ls -la -F test
 ...
 drwxr-xr-x   2 azbc  staff    64 Sep  7 16:06 dir_1/
 ...
 -rwx--x--x   1 azbc  staff     0 Sep  7 16:06 file_1.sh*

dir_1它通过链接来标识目录2

如果我列出该目录test/dir_1

 ls -la -F test/dir_1
 total 0
 drwxr-xr-x  2 azbc  staff   64 Sep  7 16:06 ./
 drwxr-xr-x  9 azbc  staff  288 Sep  7 21:37 ../

嘿,确实如此!!它列出了2条目!

该文件file_1.sh*已通过链接识别1。如果我列出文件file_1.sh

 ls -la -F test/file_1.sh
 -rwx--x--x  1 azbc  staff  0 Sep  7 16:06 test/file_1.sh*

嗬!!它确实列出了1条目!!,也就是file_1.sh它自己!并再次用条目标识该文件1

顺便问一下,我能得出结论吗?列出的每个带有1链接的条目都是文件而不是目录?然而,情况似乎并非如此,因为符号链接也被列为具有1链接/1条目。

答案1

我建议你读如果 Linux 上的所有内容都是文件,那么目录是什么?了解有关目录结构、历史和目录工作原理及其元素(inode、结构等)的术语的更深入的知识dirent,尽管这不是这个问题所必需的。

什么是点“。”和点点“..”目录?

看着format of directories 1971 年的手册页在 UNIX 程序员手册的版本中,我们看到.并且..已经存在:

按照惯例,每个目录中的前两个条目为“。”和“..”第一个是目录本身的条目。

至于其重要性,可以在以下网址找到答案:Panos 的回答肯·汤普森解释了如何..发生的1989 年采访

每次我们创建一个目录时,按照惯例,我们会将其放在另一个名为目录 - 目录的目录中,即 dd。它的名字是 dd,所有用户目录以及实际上大多数其他目录,用户维护自己的目录系统,都有指向 dd 的指针,dd 缩写为“dot-dot”,dd 代表目录-目录。它是您可以访问系统中所有其他目录以维护这个意大利面碗的地方

当然,.正如您所猜到的, 代表d或短于directory。此类目录本身自然与目录的实际名称共享相同的 inode 编号。现在,这仍然无法解释为什么目录.链接到自身,但我有几个想法。

0.Unix 哲学

在 1996 年由 Uresh Vahalia 出版的《UNIX Internals: The NEw Frontiers》一书中,第 8 章第 222 页指出:

Unix 支持每个进程的当前工作目录的概念,作为进程状态的一部分进行维护。这允许用户通过其相对路径名,它们是相对于当前目录进行解释的。

考虑到目录只是一种特殊的文件,我们需要一致的相对文件名来引用目录本身,这就是特殊的文件名.,它是由d目录的缩写演变而来的。

1.技术优势

我能想到的主要优点是系统可以简化 inode 查找,从而简化元数据信息。由于目录已经有一个包含.相同 inode 的条目,因此无需通过完整路径进行查询。编程也是如此。考虑一个非常简单实现ls.在那里我使用getcwd()函数获取当前工作目录路径,然后将其传递给opendir()。或者我可以扔掉getcwd()直接使用opendir('.')。在旧 PDP-11 终端时代,内存大小只有几千字节,节省系统调用开销至关重要。

2. 用户便利性

请考虑以下示例:

mv ../filename.txt .

在里面Hendrik Jan Thomassen 的演讲有人提到,由于旧的终端键难以按下,原始的 Unix 命令很短,因此整天输入命令实际上是一项体力活。如果您深入目录树,重新输入当前工作目录的完整路径将非常繁琐。当然,mv可以假设当我们这样做时,mv <file>我们将目标暗示为“当前工作目录”。我只能猜测为什么mv <original> <new>会盛行,也许是由于当时其他编程语言的影响。

3. 改进 MULTICS

注意:我自己从未研究过 MULTICS,因此本文仅基于阅读在线资料

根据 1986 年 MULTICS 路径名手册

相对路径名可以以一个或多个小于字符(“<”)开头。

>字符在 MULTICS 上用作路径分隔符(就像/在 Linux 上一样)。这可能看起来令人困惑。因此,./当引用命令时,可以说更清楚 - 我们引用的是位于当前工作目录中的文件名。

这可能对其他命令有用。在 Unix/Linux 上创建文件的方法众所周知:touch ./file。在 MULTICS 上,根据斯文森an通过或命令完成add_name

cd foo
r 18:03 0.041 1

an foo bar
r 18:03 0.077 3

ls foo

Directories = 1.

sma  foo
       bar

r 18:03 0.065 0

附注:谈到以下内容时有明显的相似之处..:向上导航一个目录是通过 完成的cwd <<

4. 引用可执行文件

如果你每天都运行脚本,那么你一定很了解./script.sh语法。为什么很简单:shell 的工作方式是它在PATH变量中查找可执行文件,因此当您提供./它时,它不必在任何地方查找。PATH变量的神奇之处在于它使您可以使用echo而不是/bin/echo或其他非常长的路径。现在假设您的路径中没有它script.sh,并且它位于您当前的工作目录中。你现在做什么?输入/very/long/path/to/the/executable/this/typing/gets/exhausting/on/PDP-11/finally/script.sh?这将抛弃所有 Unix 简单性的概念!所以回到 Unix 哲学,它也符合优雅设计/简单的原则。

当然,有些人想添加.PATH但这实际上是非常糟糕的做法,所以不要这么做。

边注..:和.指向相同的 特殊情况是 inode 2 - / dir ,这是有道理的,因为它是目录树中的最高点。当然,..为 NULL 也可以,但让它指向/自身更优雅。


关于链接数和目录硬链接的说明

作为吉尔斯正确指出(并引用乔治·乌德森), 目录的链接数从 2 开始(..代表父目录和.),所有附加链接均为子目录:

# new directory has link count of 2
$ stat --format=%h .
2
# Adding subdirectories increases link count
$ mkdir subdir1
$ stat --format=%h .
3
$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24

直观地看,目录的链接仅为子目录 - 这是有道理的,因为硬链接与原始文件的时间相同。不过,这些并不是真正的硬链接 - 硬链接会创建一个指向相同数据。根据该定义,指向目录的硬链接将包含相同的数据,即包含相同的文件列表。如果删除指向目录的所有硬链接,这将导致文件系统出现循环或出现大量孤立文件。因此,不允许对目录创建硬链接并使用Gilles 的措辞来自另一个问题(我建议你阅读)“...事实上,许多文件系统确实在目录上有硬链接,但只是以非常有纪律的方式......”这些是...目录的特殊情况。

现在,问题变成了目录上下文中的“链接”实际上是什么意思? TL;DR:目录结构是一棵树,这里的链接表示每个树项的子节点数(每个叶子或没有子目录的目录只有 2 个链接)。具体来说,扩展ext4使用 HTree 和軟體系用途B+ 树


结论

归根结底,原因.在于它本身的设计很好。Unix 的最初作者可能在当时的技术限制下工作,但他们是当时最聪明的人,或者通常被称为“巫师”,他们做事是有原因的。

答案2

您的问题对我来说很模糊,但我尝试解释事情的工作原理,以便帮助您理解它。

系统中存储的每个文件都有一个编号(inode编号),让我们来看看:

$ ls -i -1 -a test/
9186865 .
9175041 ..

我曾经-1在单列中显示文件列表并-i显示 inode 和-a显示隐藏文件。

每个文件都inode保存有关文件的信息,例如权限、所有者、大小、链接数、修改时间、指向实际文件数据的指针(但不是文件的名称)。

每个目录不过是一个特殊的文件,包含名称(文件)列表以及与这些名称对应的 inode。

因此,当我删除文件(也称为取消链接文件)时,我会从其父目录中删除其链接,但数据仍然存在于磁盘上。

当您创建一个新目录时,默认情况下它包含 2 个硬链接,这意味着每个目录默认在其列表中 .都有。..

您可能知道,.这是一个到当前目录的硬链接,也是..一个到父目录的硬链接,因此如果我创建一个新目录:

$ mkdir test
$ ls -i -d test
9186865 drwxrwxr-x 2 ravexina ravexina 4096 Sep  7 19:37 test

如您所见,链接数为 2,现在无论我在此目录中创建多少个文件,链接数都会保持不变,除非我开始创建目录。对于每个目录,数字将增加 1,现在您知道为什么了!因为每个新目录都包含指向其父目录的硬链接..::

还记得我说过目录吗?

每个目录不过是一个特殊的文件,包含名称(文件)列表以及与这些名称对应的 inode。

链接实际上就是这些名称,每个文件默认都有 1 个链接(创建时的名称),现在如果您为此文件创建一个新的硬链接(意味着另一个名称,在另一个目录或指向相同数据 [inode] 的同一目录中),该数字将增加 1。

答案3

摘自Gilles 的出色回答

历史上,第一个 Unix 文件系统在每个目录中创建了两个条目:.指向目录本身,并..指向其父目录。这为应用程序和操作系统本身提供了一种遍历文件系统的简单方法。

因此,每个目录的链接数为 2+n,其中 n 是子目录的数量。链接是该目录在其父目录中的条目、目录自己的.条目以及..每个子目录中的条目。例如,假设这是以 为根的子树的内容/parent,所有目录:

/parent
/parent/dir
/parent/dir/sub1
/parent/dir/sub2
/parent/dir/sub3

然后dir的链接数为 5:dir中的条目/parent.中的条目以及、和中各/parent/dir三个条目。由于没有子目录,其链接数为 2(中的条目和中的条目)。../parent/dir/sub1/parent/dir/sub2/parent/dir/sub3/parent/dir/sub1sub1/parent/dir./parent/dir/sub1

为了尽量减少根目录的特殊情况(因为没有“正确”的父目录),根目录包含一个..指向自身的条目。这样,它的链接数也为 2 加上子目录数,2 为/./..

后来的文件系统倾向于在内存中跟踪父目录,通常不需要...作为实际条目存在;典型的现代 unix 系统将.和视为..与文件系统类型无关的文件系统代码的特殊值。一些文件系统仍然包含...条目,或者假装包含和条目,即使磁盘上没有显示任何内容。

大多数文件系统仍然报告目录的链接数为 2+n,无论是否存在.条目..,但也有例外,例如 btrfs 就不会这样做。

现在尝试在评论中回答您的疑问:

这里列出的链接是什么?是文件本身吗?还是包含该文件的目录被列出?

该链接属于文件而不是目录,让我用一个例子来说明这一事实。现在如果我要做nano file.txt,这里的链接将用于定位inode该文件的编号,inode随后将提供使程序能够修改该文件的信息nano。请记住,保存inode有关该文件的信息。(无论是文件夹、文件还是块设备)

现在,每个文件名都必须链接到一个,inode number才能对该文件执行正常操作,所以是的,该链接属于该文件,而不是父文件。我希望我正确理解了你的问题并回答了它。

相关内容