上一篇文章简单介绍了文件系统的基础知识,这篇继续学习文件系统的内容。
创建文件的过程
文件有内容和属性,内核将文件内容存放在数据区,文件属性存放在 i 节点,文件名存放在目录中。
创建一个新文件的主要操作步骤如下:
1、存储属性
先找到一个空的 i 节点,然后内核把文件的信息记录在这个 i 节点中。
2、存储数据
根据文件内容的大小,申请能够存储这个文件的磁盘块(假如需要 n 个磁盘块)。内核从空闲磁盘块列表中找出 n 个自由块。将文件内容存放在这些自由磁盘块中。
3、记录磁盘块分配情况
文件内容按顺序存放在内核分配的磁盘块中。内核在 i 节点的磁盘分区记录块序列。
4、添加文件名到目录
内核将文件 i 节点和文件名添加到目录文件。文件名和 i 节点之间的对应关系,将文件名和文件的内容及属性连接了起来。
目录的工作过程
目录是一种包含了文件名字列表的特殊文件,是一个包含 i 节点和文件名的表。例如:
i-节点号 | 文件名 |
---|---|
4567 | . |
237654 | … |
14327 | hello.c |
634521 | demo.c |
1、目录内部
可以通过命令 ls -1ia
来查看某个目录的内容:
$ ls -1ia demodir/
4889618 .
4751672 ..
9342328 a
9716876 c
显示目录下所有文件及其对应的 i 节点。例如,当前目录 “.” 的 i 节点号是 4889618。这意味着,有关大小、文件所有者、组等各项关于当前目录的信息,存放在 i 节点表中的编号为 4889618 的结构中。
ls 的选项 -1(数字 1)要求每行列出一个文件;选项 -i 告诉 ls 显示 i 节点号;选项 -a 要求显示所有文件。
2、指向同一文件的多重链接
可以使用命令 ls -i
查看系统上任何一个文件的 i 节点号。例如查看系统上根目录中各个文件的 i 节点号:
$ ls -ia /
2 . 3670017 lib 3145729 sbin
2 .. 3276801 lib32 5242881 snap
9437185 bin 3014657 lib64 11796481 srv
2883585 boot 11 lost+found 12 swapfile
2490369 cdrom 2359297 media 1 sys
2 dev 4718593 mnt 11010049 tmp
11272193 etc 10747905 opt 7208961 usr
4194305 home 1 proc 2228225 var
14 initrd.img 7077889 root 16 vmlinuz
15 initrd.img.old 2 run 13 vmlinuz.old
457654 test.log 457654 hello.log
- 文件具有相同的 i 节点号
左下角的两个文件 test.log 和 hello.log 拥有相同的 i 节点号 457654。两个文件都指向了同一个 i 节点。i 节点实际上代表了一个文件,包含了文件的属性和数据块的列表。因此,test.log 和 hello.log 是同一个文件的两个不同名字。
- 目录具有相同的 i 节点号
左上角的 “.” 和 “…”,这两项 i 节点号都为 2。所以,“.” 和 “…” 都指向同一个目录。根目录比较特殊,当用命令 mkfs 创建一个文件系统时,mkfs 将根目录的父目录指向自己。
读取文件的过程
读取一个文件时会发生什么呢?如下指令:
$ cat hello.c
1、在目录中寻找文件名
文件名存储在目录文件中。内核在目录文件中搜索包含字符串 “hello.c” 的记录。假设其所在的记录包含编号为 23567 的 i 节点号。
2、定位 i 节点并读取其内容
内核在文件系统中的 i 节点区域找到 i 节点 23567。定位 i 节点可能需要一些简单的计算,所有 i 节点大小相同,每个磁盘块都包含相同数量的 i 节点。为了提高访问效率,内核可能会将 i 节点置于缓冲区中。i 节点包含数据块编号的列表。
3、访问存储文件内容的数据块
通过以上两步,内核已经可以知道文件内容存放在哪些磁盘块上,以及它们的顺序。由于 cat 指令不断地调用 read() 函数,使得内核不断将字节从磁盘块复制到内核缓冲区中,进而到达用户空间。
多个文件系统的组合
用一些简单的抽象能够将一个单一的分区组织成一个目录树。但是,如果有两个分区,该怎样处理呢?
有些操作系统将盘符或卷标分配给每个磁盘或分区,并将字母或名字作为一个文件全路径的一部分。还有一种做法是,有些系统统一给所有的磁盘块分配编号,以创建一个虚拟的单一磁盘。
Linux 使用第三种方法:每个分区有自己的文件系统树。当计算机上有多于一个的文件系统时,Linux 提供了一种方法将这些树整合成一棵更大的树。
假设两个磁盘上分别有一个目录树,每棵树都有一个根目录。其中一个文件系统被命名为根文件系统,这棵树的顶端是整棵大树的真正的根;另一个文件系统则被附加到根文件系统的某个子目录上。
在内部,内核将根文件系统中的一个目录作为指针,指向另一个文件系统的根,这样两个文件系统就联系起来了。
- 装载点
在 Linux 系统中,装载文件系统是指,将它嵌入到已有的系统中,以获得某些支持。子树的根目录被嵌入到文件系统的一个目录中,子树所在的目录被称为第二个系统的装载点。
命令 mount 列出当前装载的文件系统以及它们的装载点:
$ mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
udev on /dev type devtmpfs (rw,nosuid,relatime,size=1995672k,nr_inodes=498918,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)
tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=403936k,mode=755)
/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/dev/sda1 on /
表明 /dev/hda 上的分区 1 被装载在树的根目录上,这个分区是跟文件系统。
Linux 允许不同类型的文件系统被装载在根文件系统上。
- 多重 i 节点号和设备交叉链接
Linux 每个文件都有一个 i 节点号。两个不同的磁盘可能都含有 i 节点号为 23567 的文件。若干个目录可能都含有 i 节点号为 23567 的文件,内核是如何知道应该使用哪个 23567 的 i 节点呢?
在两个磁盘上的文件系统中,各有一个文件,且它们的 i 节点号相同。实际上,这两个链接根本不指向同一个文件。而是分别指向各自磁盘系统上的文件(虽然看起来像指向同一个文件)。
不同的文件系统不能生成指向同一个文件的链接,文件以数据块的集合和一个 i 节点的形式存储在磁盘上,目录中的链接会指向文件的 i 节点。
- 符号链接
硬链接是将目录连接到树的指针,也是将文件名和文件本身链接起来的指针。
符号链接通过名字引用文件,而不是 i 节点号。符号链接可以跨越文件系统,因为它们并不存储原始文件的 i 节点。符号链接也可以指向目录,因为它们与将文件系统联系在一起的真正的链接不同。
硬链接与符号链接之间的区别举例,通过 ln 创建硬链接和符号链接:
$ ln test hard_lnk /* 创建一个硬链接 hard_link,指向文件 test */
$ ln -s test sym_lnk /* 创建一个符号链接 sym_lnk,指向文件 test */
$ ls -li /* 显示文件 i 节点和文件信息 */
8914203 -rw-rw-r-- 2 user user 517 12月 7 13:00 hard_lnk
8914210 lrwxrwxrwx 1 user user 4 12月 7 13:16 sym_lnk -> test
8914203 -rw-rw-r-- 2 user user 517 12月 7 13:00 test
创建一个硬链接文件 hard_lnk,文件 test 和 hard_lnk 实际上指向同一个文件,具有相同的 i 节点号 8914203,也具有同样的文件大小、修改时间和链接数。
指令 ln -s
创建文件 test 的一个符号链接 sym_lnk。这个符号链接的 i 节点为 8914210。其链接数、修改时间和文件大小都不同于原始文件。但当对文件 sym_lnk 进行读写时,实际上操作的是原始文件。
如果原始文件被删除,或者原始文件修改了文件名,符号链接将指向空。
小结
- 文件属性存储在 i 节点,文件内容存储在磁盘的数据块。i 节点号是文件的唯一标识。
- 相同的 i 节点可能以不同的名字在若干个目录中出现。每个入口被称为指向文件的硬链接。
- 符号链接是通过文件名引用文件,而不是 i 节点号。
- 若干个文件系统的目录树可以被整合成一棵树。内核将一个文件系统的目录,链接到另一个文件系统的根目录的操作,称为装载。
关注公众号【一起学嵌入式】,获取更多精彩内容