初学者通常会听到一个短语:“一切都是Linux / Unix上的文件”。但是,目录是什么?它们与文件有何不同?
初学者通常会听到一个短语:“一切都是Linux / Unix上的文件”。但是,目录是什么?它们与文件有何不同?
Answers:
注意:最初是为了支持我的答案而写的,为什么ls
我将命令中的当前目录标识为链接到自身?但是我认为这是一个值得独立思考的主题,因此值得一问。
本质上,目录只是一个特殊文件,其中包含条目列表及其ID。
在开始讨论之前,重要的是要区分几个术语并了解目录和文件的真正含义。您可能已经听说过Unix / Linux的表达“一切都是文件”。嗯,用户通常将其理解为文件:/etc/passwd
-具有路径和名称的对象。实际上,名称(无论是目录还是文件,还是其他名称)只是一串文本-实际对象的属性。该对象称为inode或I编号,并存储在inode表中的磁盘上。开放程序也有inode表,但这不是我们现在关心的。
Unix的目录概念就像Ken Thompson在1989年的一次采访中所说的那样:
...然后其中一些文件是仅包含名称和I-号码的目录。
丹尼斯·里奇(Dennis Ritchie)在1972年的讲话中可以得出有趣的观察,
“ ...目录实际上只不过是一个文件,但是其内容由系统控制,并且内容是其他文件的名称。(在其他系统中,目录有时也称为目录。”)
...但是谈话中没有提到inode。然而,1971年手动上format of directories
指出:
文件是目录这一事实由其i节点条目的标志字中的一位指示。
目录条目的长度为10个字节。第一个单词是条目所代表的文件的i节点(如果非零);如果为零,则该条目为空。
从一开始它就一直在那里。
目录和索引节点的配对在UNIX文件系统中如何存储目录结构中也有说明。。目录本身是一种数据结构,更具体地说:一个对象列表(文件和索引节点编号),指向这些对象的列表(权限,类型,所有者,大小等)。因此,每个目录都包含其自己的索引节点号,然后是文件名及其索引节点号。最著名的是inode#2,它是/
directory。(请注意,尽管这/dev
并/run
是虚拟的文件系统,所以因为他们为自己的文件系统的根文件夹中,他们也有索引节点2; 即,一个inode在其自己的fileystem上是唯一的,但是附加了多个文件系统,则您具有非唯一的inode。从链接的问题中借用的图表可能更简洁地解释了它:
可以通过以下方式访问存储在索引节点中的所有信息: stat()
按照Linux,系统调用man 7 inode
:
每个文件都有一个包含有关该文件的元数据的索引节点。应用程序可以使用返回一个stat结构的stat(2)(或相关调用)或返回一个statx结构的statx(2)来检索此元数据。
是否可以仅知道其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域套接字确实在文件系统上具有文件名。
数据本身可能是唯一的,但inode编号不是唯一的。如果我们有一个指向foobar的硬链接foobar,那么它也将指向inode 123。该索引节点本身包含有关该索引节点占用哪些实际磁盘空间块的信息。从技术上讲,这就是您如何.
链接到目录文件名的方式。好吧,几乎:您自己不能在Linux上创建到目录的硬链接,但是文件系统可以以非常严格的方式允许到目录的硬链接,这限制了仅具有.
,并..
用硬链接。
文件系统将目录树实现为树数据结构之一。尤其是,
这里的关键是目录本身是树中的节点,子目录是子节点,每个子节点都有一个指向父节点的链接。因此,对于目录链接,裸目录(链接到目录名/home/example/
和链接到self /home/example/.
)的inode数量最少为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
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24
在Ian D.Allen的课程页面上找到的图显示了一个简化的非常清晰的图:
WRONG - names on things RIGHT - names above things
======================= ==========================
R O O T ---> [etc,bin,home] <-- ROOT directory
/ | \ / | \
etc bin home ---> [passwd] [ls,rm] [abcd0001]
| / \ \ | / \ |
| ls rm abcd0001 ---> | <data> <data> [.bashrc]
| | | |
passwd .bashrc ---> <data> <data>
RIGHT图表中唯一不正确的是,从技术上讲,文件不属于目录树本身:添加文件对链接数没有影响:
$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4
“一切都是文件”的重点并不是您拥有一些随机的文件名(实际上,套接字和管道表明“文件”和“文件名”彼此无关),而是可以使用通用文件名这一事实。用于处理不同事物的工具。
考虑到目录只是文件的一种特殊情况,自然必须有一些API允许我们打开 / 读取 / 写入 / 关闭与常规文件类似的方式它们。
这就是dirent.h
C库的位置,它定义了dirent
结构,您可以在man 3 readdir中找到该结构:
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
如上所示。
我如何在当前工作目录中列出文件及其索引节点编号的答案中可以找到有关此方法工作原理的实际示例。
请注意,关于fdopen的POSIX手册指出“点和点的目录条目是可选的”,而readdir手册状态 struct dirent
仅要求具有d_name
和d_ino
字段。
关于“写入”目录的注意事项:写入目录正在修改其“条目列表”。因此,创建或删除文件与目录写权限直接相关,添加/删除文件是在该目录上的写操作。
open()
,read()
套接字也有connect()
和read()
。更为准确的是,“文件”实际上是存储在磁盘或内存中的有组织的“数据”,并且某些文件是匿名的-它们没有文件名。通常,用户会根据桌面上的图标来思考文件,但这并不是唯一的存在。另请参见unix.stackexchange.com/a/116616/85039