# 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
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
因此,在您的 C 代码中,您必须定义struct dirent *entry_p,并且当我们打开目录opendir()并使用 开始读取它时readdir(),我们会将每个项目存储到该entry_p结构中。当然,每个项目都将包含dirent上面显示的模板中定义的字段。
注意:最初这是为了支持我对为什么命令中的当前目录被
ls
标识为链接到自身的答案而编写的?但我觉得这是一个值得独立存在的话题,因此有这个问答。了解 Unix/Linux 文件系统和文件:一切都是 inode
本质上,目录只是一个特殊文件,其中包含条目列表及其 ID。
在我们开始讨论之前,重要的是要区分几个术语并了解目录和文件的真正含义。您可能听说过 Unix/Linux 的“一切都是文件”的说法。嗯,用户通常理解为文件是这样的:
/etc/passwd
- 具有路径和名称的对象。实际上,名称(无论是目录、文件还是其他)只是一串文本 - 实际对象的属性。该对象称为inode或 I-number,并存储在磁盘上的 inode 表中。开放程序也有 inode 表,但这不是我们现在关心的问题。Unix 的目录概念就像 Ken Thompson 在1989 年的一次采访中所说的那样:
从丹尼斯·里奇 1972 年的演讲中可以得出一个有趣的观察结果:
...但是谈话中没有提到 inode 的任何地方。然而,1971 年的手册指出
format of directories
:所以它从一开始就在那里。
目录和 inode 配对也在UNIX 文件系统中的目录结构如何存储?. 目录本身是一种数据结构,更具体地说:对象列表(文件和 inode 编号)指向有关这些对象的列表(权限、类型、所有者、大小等)。所以每个目录都包含自己的 inode 编号,然后是文件名及其 inode 编号。最著名的是inode #2,它是
/
directory。(注意,虽然/dev
和/run
是虚拟文件系统,所以由于它们是文件系统的根文件夹,它们也有 inode 2; 即一个inode 在它自己的文件系统上是唯一的,但是附加了多个文件系统,你有非唯一的inode)。从链接问题借来的图表可能更简洁地解释了它:stat()
根据 Linux,存储在 inode 中的所有信息都可以通过系统调用访问man 7 inode
:是否可以仅知道其 inode 编号( ref1,ref2 )来访问文件?在某些 Unix 实现上是可能的,但它绕过了权限和访问检查,所以在 Linux 上它没有实现,你必须遍历文件系统树(
find <DIR> -inum 1234
例如通过)来获取文件名及其对应的 inode。在源代码级别,它在Linux 内核源代码中定义,也被许多在 Unix/Linux 操作系统上运行的文件系统采用,包括 ext3 和 ext4 文件系统(Ubuntu 默认)。有趣的是:由于数据只是信息块,Linux 实际上有inode_init_always 函数可以确定一个 inode 是否是管道 (
inode->i_pipe
)。是的,套接字和管道在技术上也是文件——匿名文件,在磁盘上可能没有文件名。FIFO和Unix-Domain 套接字在文件系统上确实有文件名。数据本身可能是唯一的,但 inode 编号不是唯一的。如果我们有一个名为 foobar 的指向 foo 的硬链接,那么它也将指向 inode 123。该 inode 本身包含有关该 inode 占用了哪些实际磁盘空间块的信息。从技术上讲,这就是您可以
.
链接到目录文件名的方式。好吧,几乎:您不能自己在 Linux 上创建指向目录的硬链接,但是文件系统可以以非常规范的方式允许指向目录的硬链接,这限制了只有硬链接.
和..
硬链接。目录树
文件系统将目录树实现为树数据结构之一。尤其是,
这里的关键点是目录本身是树中的节点,子目录是子节点,每个子节点都有一个返回父节点的链接。因此,对于目录链接,裸目录(指向目录名称的
/home/example/
链接和指向 self的链接/home/example/.
)的 inode 计数最少为 2,并且每个额外的子目录都是一个额外的链接/节点:Ian D. Allen 的课程页面上的图表显示了一个简化的非常清晰的图表:
右图中唯一不正确的是文件在技术上不被认为位于目录树本身:添加文件对链接计数没有影响:
像访问文件一样访问目录
引用Linus Torvalds 的话:
考虑到目录只是文件的一种特殊情况,自然必须有 API 允许我们以与常规文件类似的方式打开/读取/写入/关闭它们。
这就是
dirent.h
C 库出现的地方,它定义了dirent
结构,您可以在man 3 readdir中找到它:因此,在您的 C 代码中,您必须定义
struct dirent *entry_p
,并且当我们打开目录opendir()
并使用 开始读取它时readdir()
,我们会将每个项目存储到该entry_p
结构中。当然,每个项目都将包含dirent
上面显示的模板中定义的字段。可以在我关于如何在当前工作目录中列出文件及其 inode 编号的回答中找到有关其工作原理的实际示例。
请注意,关于 fdopen 的 POSIX 手册指出“点和点点的目录条目是可选的”,而readdir 手动状态
struct dirent
只需要具有d_name
和d_ino
字段。关于“写入”目录的注意事项:写入目录正在修改其条目“列表”。因此,创建或删除文件与目录写入权限直接相关,添加/删除文件是对该目录的写入操作。