这是关于通用UFS。
据我了解,当给出绝对路径(例如:)时,/home/userU/file.txt
每个目录和文件都会访问磁盘。因此在这种情况下磁盘被访问了 4 次
1 个/
, 1 个home/
, 1 个/userU
, 1 个file.txt
我的问题是
- 如果给出硬链接
/hL
,指向上述文件的inode,那么磁盘访问的顺序是什么? - 如果给出一个软链接
/sL
,指向上面的文件,那么磁盘访问的顺序是什么?
假设在所有三种情况下最初都没有缓存任何 inode 或任何其他数据。
答案1
背景
假设我们有以下目录设置:
$ ll
total 0
-rw-r--r-- 2 root root 0 Jul 29 23:36 afile.txt
-rw-r--r-- 2 root root 0 Jul 29 23:36 hL
lrwxrwxrwx 1 root root 9 Jul 30 01:22 sL -> afile.txt
现在我们来看看你的 2 个问题。
问题
- 如果给出一个硬链接/hL,指向上述文件的inode,那么磁盘访问的顺序是什么?
通过硬链接,它们拥有与它们指向的原始文件/目录相同的索引节点引用。因此没有额外的 HDD 访问权限来读取它们。
例如:
$ stat hL | head -3
File: ‘hL’
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fd00h/64768d Inode: 667668 Links: 2
与
$ stat afile.txt | head -3
File: ‘afile.txt’
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fd00h/64768d Inode: 667668 Links: 2
这两个之间的唯一区别是名称。因此,任一路径都会产生相同数量的 HDD 访问。
- 如果给出软链接/sL,指向上面的文件,那么磁盘访问的顺序是什么?
然而,通过软链接,可以进行额外的 HDD 访问。这种额外的访问将针对文件所在目录的元数据sL
。然后,这将返回详细信息,说明该文件实际上是符号链接,并且它指向另一个文件/目录。
例如:
$ stat sL | head -3
File: ‘sL’ -> ‘afile.txt’
Size: 9 Blocks: 0 IO Block: 4096 symbolic link
Device: fd00h/64768d Inode: 681295 Links: 1
在这里我们可以看到它的类型是“符号链接”并且它指向afile.txt
.还要注意它有一个不同的 inode(681295 与 667668),进一步证明它将花费额外的读取费用。
那么读取顺序是什么呢?
如果您使用strace
针对这些文件/目录运行命令的 Bash shell 本身,您可以了解事情是如何工作的。
[pid 18098] stat("/tmp/adir/hL", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
[pid 18098] open("/tmp/adir/hL", O_RDONLY) = 3
[pid 18098] fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
这是命令的输出more /tmp/adir/hL
。
为了/tmp/adir/hL
:
- 统计/打开 (/) → 统计/打开 (tmp) → 统计/打开 (adir) → 统计/打开 (hL)
为了/tmp/adir/sL
:
- 统计/打开 (/) → 统计/打开 (tmp) → 统计/打开 (adir) → 统计/打开 (sL) → 统计/打开 (afile.txt)
更多细节
这符号链接的维基百科页面也回避了这一切:
虽然将链接值存储在 inode 内可以节省磁盘块和磁盘读取,但操作系统仍然需要解析链接中的路径名,这总是需要读取额外的 inode,并且通常需要读取其他(可能是许多)目录、处理文件列表和每个文件的索引节点,直到找到与链接的路径组件匹配的内容。仅当链接指向同一目录中的文件时,“快速符号链接”才提供比其他符号链接明显更好的性能。
参考
答案2
这两个问题其实都是一个问题:“如何path_resolution
运作?”,所以就从这个角度来看整个过程。
从路径分辨率(7)我们读:
文件名(或路径名)的解析如下。
然后我们看到第一步对于硬链接和符号链接都是通用的(系统决定路径解析的起点:根目录/
、chrooted 目录或当前目录)。
如果路径名以“/”字符开头,则起始查找目录是调用进程的根目录。 (进程从其父进程继承根目录。通常这将是文件层次结构的根目录。进程可以通过使用 chroot(2) 系统调用获得不同的根目录。进程可以获得完全私有的挂载命名空间,以防它(或其祖先之一)是通过调用设置了 CLONE_NEWNS 标志的 Clone(2) 系统调用启动的。)这处理路径名的“/”部分。
如果路径名不以“/”字符开头,则解析进程的起始查找目录是该进程的当前工作目录。 (这也是从父级继承的。它可以通过使用 chdir(2) 系统调用来更改。)
以“/”字符开头的路径名称为绝对路径名。不以“/”开头的路径名称为相对路径名。
正如我们所看到的,硬链接和符号链接之间的起点没有区别。但是,当开始行走路径时,下一步确实会出现差异:
将当前查找目录设置为起始查找目录。现在,对于路径名的每个非最终组件(其中组件是由“/”字符分隔的子字符串),将在当前查找目录中查找该组件。
如果进程没有当前查找目录的搜索权限,则会返回 EACCES 错误(“权限被拒绝”)。
如果未找到该组件,则返回 ENOENT 错误(“没有此类文件或目录”)。如果找到该组件,但既不是目录也不是符号链接,则返回 ENOTDIR 错误(“不是目录”)。
如果找到该组件并且是一个目录,我们将当前查找目录设置为该目录,然后转到下一个组件。
正如描述所示,文件和硬链接的路径解析没有区别 - 过程是相同的。那么符号链接呢?我们进一步阅读:
如果找到该组件并且是符号链接(symlink),我们首先解析这个符号链接(以当前查找目录作为起始查找目录)。发生错误时,将返回该错误。如果结果不是目录,则返回 ENOTDIR 错误。如果符号链接解析成功并返回一个目录,我们将当前查找目录设置为该目录,然后转到下一个组件。请注意,如果路径名的前缀(“dirname”)组件包含一个文件名,该文件名是解析为目录的符号链接(其中该目录的前缀组件可能包含符号链接,因此,这里的解析过程可能涉及递归)在)。为了保护内核免受堆栈溢出的影响,并防止拒绝服务,对最大递归深度和遵循的最大符号链接数有限制。当超过最大值时,会返回 ELOOP 错误(“符号链接级别过多”)。
如上所述,符号链接解析需要额外的磁盘访问操作,因此回答这两个问题:
如果给出一个硬链接/hL,指向上述文件的inode,那么磁盘访问的顺序是什么?
和
如果给出软链接/sL,指向上面的文件,那么磁盘访问的顺序是什么?
我们可以得出结论,硬链接访问与普通文件访问没有区别,但符号链接解析需要额外的磁盘访问操作,即符号链接解析。
补充阅读: