文件系统中文件操作流程

转载于骏马金龙

单文件系统中文件操作的原理

在Linux上执行删除、复制、重命名、移动等操作时,它们是怎么进行的呢?还有访问文件时是如何找到它的呢?

读取文件

当执行cat /var/log/messages命令在系统内部进行了什么样的步骤呢?该命令能被成功执行涉及了cat命令的寻找、权限判断以及messages文件的寻找和权限判断等等复杂的过程。这里只解释内容相关的如何寻找到被cat的/var/log/messages文件。

  1. 找到根文件系统的块组描述符表所在的blocks,读取GDT(已在内存中)找到inode table的block号。
    因为GDT总是和superblock在同一个块组,而superblock总是在分区的第1024-2047个字节,所以很容易就知道第一个GDT所在的块组以及GDT在这个块组中占用了哪些block。
    其实GDT早已经在内存中了,在系统开机的时候会挂载根文件系统,挂载的时候就已经将所有的GDT放进内存中。
  2. 在inode table的block中定位到根“/”的inode,找出“/”指向的data block
    ext文件系统预留了一些inode号,其中"/"的inode号为2,所以可以根据inode号直接定位根目录文件的data block。
  3. 在"/"的datablock中记录了var目录名和var的inode号,找到该inode记录,inode记录中存储了指向var的block指针,所以也就找到了var目录文件的data block
    通过var目录的inode号,可以寻找到var目录的inode记录,但是在寻找的过程中,还需要知道该inode记录所在的块组以及所在的inode table,所以需要读取GDT,同样,GDT已经缓存到了内存中。
  4. 在"/"的datablock中记录了var目录名和var的inode号,找到该inode记录,inode记录中存储了指向var的block指针,所以也就找到了var目录文件的data block
  5. 在log目录文件的data block中记录了messages文件名和对应的inode号,通过该inode号定位到该inode所在的块组及所在的inode table,并根据该inode记录找到messages的data block
  6. 最后读取messages对应的datablock。

将上述步骤中GDT部分的步骤简化后比较容易理解。如下: 找到GDT-->找到"/"的inode-->找到/的数据块读取var的inode-->找到var的数据块读取log的inode-->找到log的数据块读取messages的inode-->找到messages的数据块并读取它们。

当然,在每次定位到inode记录后,都会先将inode记录加载到内存中,然后查看权限,如果权限允许,将根据block指针找到对应的data block。

删除、重命名和移动文件

注意这里是不跨越文件系统的操作行为。

删除文件分为普通文件和目录文件,知道了这两种类型的文件的删除原理,就知道了其他类型特殊文件的删除方法

对于删除普通文件:

  1. 找到文件的inode和data block(根据前一个小节中的方法寻找)
  2. 将inode table中该inode记录中的data block指针删除
  3. 在imap中将该文件的inode号标记为未使用
  4. 在其所在目录的data block中将该文件名所在的记录行删除,删除了记录就丢失了指向inode的指针(实际上不是真的删除,直接删除的话会在目录data block的数据结构中产生空洞,所以实际的操作是将待删除文件的inode号设置为特殊的值0,这样下次新建文件时就可以重用该行记录)
  5. 将bmap中data block对应的block号标记为未使用

对于删除目录文件:

  1. 找到目录和目录下所有文件、子目录、子文件的inode和data block
  2. 在imap中将这些inode号标记为未使用
  3. 将bmap中将这些文件占用的 block号标记为未使用
  4. 在该目录的父目录的data block中将该目录名所在的记录行删除

需要注意的是,删除父目录data block中的记录是最后一步,如果该步骤提前,将报目录非空的错误,因为在该目录中还有文件占用。

关于上面的2-5:当2中删除data block指针后,将无法再找到这个文件的数据;当3标记inode号未使用,表示该inode号可以被后续的文件重用;当4删除目录data block中关于该文件的记录,真正的删除文件,外界再也定位也无法看到这个文件了;当5标记data block为未使用后,表示开始释放空间,这些data block可以被其他文件重用。

注意,在第5步之前,由于data block还未被标记为未使用,在superblock中仍然认为这些data block是正在使用中的。这表示尽管文件已经被删除了,但空间却还没有释放,df也会将其统计到已用空间中(df是读取superblock中的数据块数量,并计算转换为空间大小)。

什么时候会发生这种情况呢?当一个进程正在引用文件时将该文件删除,就会出现文件已删除但空间未释放的情况。这时步骤已经进行到4,外界无法再找到该文件,但由于进程在加载该文件时已经获取到了该文件所有的data block指针,该进程可以获取到该文件的所有数据,但却暂时不会释放该文件空间。直到该进程结束,文件系统才将未执行的步骤5继续完成。这也是为什么有时候du的统计结果比df小的原因,关于du和df统计结果的差别,详细内容见:详细分析du和df的统计结果为什么不一样

重命名文件分为同目录内重命名和非同目录内重命名。非同目录内重命名实际上是移动文件的过程

同目录内重命名文件的动作仅仅只是修改所在目录data block中该文件记录的文件名部分,不是删除再重建的过程。

如果重命名时有文件名冲突(该目录内已经存在该文件名),则提示是否覆盖。覆盖的过程是覆盖目录data block中冲突文件的记录。例如/tmp/下有a.txt和a.log,若将a.txt重命名为a.log,则提示覆盖,若选择覆盖,则/tmp的data block中关于a.log的记录被覆盖。

同文件系统下移动文件实际上是修改目标文件所在目录的data block,向其中添加一行指向inode table中待移动文件的inode指针,如果目标路径下有同名文件,则会提示是否覆盖,实际上是覆盖目录data block中冲突文件的记录,由于同名文件的inode记录指针被覆盖,所以无法再找到该文件的data block,也就是说该文件被标记为删除(如果多个硬链接数,则另当别论)。

所以在同文件系统内移动文件相当快,仅仅在所在目录data block中添加或覆盖了一条记录而已。也因此,移动文件时,文件的inode号是不会改变的。

对于不同文件系统内的移动,相当于先复制再删除的动作。见后文。

存储和复制文件

对于文件存储

  1. 读取GDT,找到各个(或部分)块组imap中未使用的inode号,并为待存储文件分配inode号

  2. 在inode table中完善该inode号所在行的记录

  3. 在目录的data block中添加一条该文件的相关记录

  4. 将数据填充到data block中
    注意,填充到data block中的时候会调用block分配器:一次分配4KB大小的block数量,当填充完4KB的data block后会继续调用block分配器分配4KB的block,然后循环直到填充完所有数据。也就是说,如果存储一个100M的文件需要调用block分配器100*1024/4=25600次。

    另一方面,在block分配器分配block时,block分配器并不知道真正有多少block要分配,只是每次需要分配时就分配,在每存储一个data block前,就去bmap中标记一次该block已使用,它无法实现一次标记多个bmap位。这一点在ext4中进行了优化。

  5. 填充完之后,去inode table中更新该文件inode记录中指向data block的寻址指针

对于复制,完全就是另一种方式的存储文件。步骤和存储文件的步骤一样。

多文件系统关联

在单个文件系统中的文件操作和多文件系统中的操作有所不同

根文件系统的特殊性

这里要明确的是,任何一个文件系统要在Linux上能正常使用,必须挂载在某个已经挂载好的文件系统中的某个目录下,例如/dev/cdrom挂载在/mnt上,/mnt目录本身是在"/"文件系统下的。而且任意文件系统的一级挂载点必须是在根文件系统的某个目录下,因为只有"/"是自引用的。这里要说明挂载点的级别和自引用的概念。

假如/dev/sdb1挂载在/mydata上,/dev/cdrom挂载在/mydata/cdrom上,那么/mydata就是一级挂载点,此时/mydata已经是文件系统/dev/sdb1的入口了,而/dev/cdrom所挂载的目录/mydata/cdrom是文件系统/dev/sdb1中的某个目录,那么/mydata/cdrom就是二级挂载点。一级挂载点必须在根文件系统下,所以可简述为:文件系统2挂载在文件系统1中的某个目录下,而文件系统1又挂载在根文件系统中的某个目录下。

再解释自引用。首先要说的是,自引用的只能是文件系统,而文件系统表现形式是一个目录,所以自引用是指该目录的data block中,"."和".."的记录中的inode号都对应inode table中同一个inode记录,所以它们inode号是相同的,即互为硬链接。而根文件系统是唯一可以自引用的文件系统。

由此也能解释cd /.和cd /..的结果都还是再根下,这是自引用最直接的表现形式。

挂载文件系统的细节

挂载文件系统到某个目录下,例如mount /dev/cdrom /mnt,挂载成功后,/mnt目录中的文件全部暂时不可见了,且挂载后权限和所有者(如果指定允许普通用户挂载)等的都改变了,这是为什么?

下面就以通过mount /dev/cdrom /mnt为例,详细说明挂载过程中涉及的细节。

在将文件系统/dev/cdrom(此处暂且认为它是文件系统)挂载到挂载点/mnt之前,挂载点/mnt是根文件系统中的一个目录,"/"的data block中记录了/mnt的一些信息,其中包括inode号inode_n,而在inode table中,/mnt对应的inode记录中又存储了block指针block_n,此时这两个指针还是普通的指针。

0000000.png

当文件系统/dev/cdrom挂载到/mnt上后,/mnt此时就已经成为另一个文件系统的入口了,因此它需要连接两边文件系统的inode和data block。但是如何连接呢?如下图。

11111111.png

在根文件系统的inode
table中,为/mnt重新分配一个inode记录m,该记录的block指针block_m指向文件系统/dev/cdrom中的data
block。既然为/mnt分配了新的inode记录m,那么在"/"目录的data block中,也需要修改其inode指针为inode_m以指向m记录。同时,原来inode table中的inode记录n就被标记为暂时不可用。

(注意区分inode number和inode pointer)

block_m指向的是文件系统/dev/cdrom的data block,所以严格说起来,除了/mnt的元数据信息即inode记录m还在根文件系统上,/mnt的data block已经是在/dev/cdrom中的了。这就是挂载新文件系统后实现的跨文件系统,它将挂载点的元数据信息和数据信息分别存储在不同的文件系统上。

挂载完成后,将在/proc/self/{mounts,mountstats,mountinfo}这三个文件中写入挂载记录和相关的挂载信息,并会将/proc/self/mounts中的信息同步到/etc/mtab文件中,当然,如果挂载时加了-n参数,将不会同步到/etc/mtab。

而卸载文件系统,其实质是移除临时新建的inode记录(当然,在移除前会检查是否正在使用)及其指针,并将指针指回原来的inode记录,这样inode记录中的block指针也就同时生效而找回对应的data block了。由于卸载只是移除inode记录,所以使用挂载点和文件系统都可以实现卸载,因为它们是联系在一起的。

多文件系统操作关联

假如下图中的圆代表一块硬盘,其中划分了3个区即3个文件系统。其中根是根文件系统,/mnt是另一个文件系统A的入口,A文件系统挂载在/mnt上,/mnt/cdrom也是一个文件系统B的入口,B文件系统挂载在/mnt/cdrom上。每个文件系统都维护了一些inode table,这里假设图中的inode table是每个文件系统所有块组中的inode table的集合表。

2222222222222222222222222222222222222.png

如何读取/var/log/messages呢?这是和"/"在同一个文件系统的文件读取,在前面单文件系统中已经详细说明了。

但如何读取A文件系统中的/mnt/a.log呢?首先,从根文件系统找到/mnt的inode记录,这是单文件系统内的查找;然后根据此inode记录的block指针,定位到/mnt的data block中,这些block是A文件系统的data block;然后从/mnt的data block中读取a.log记录,并根据a.log的inode指针定位到A文件系统的inode table中对应a.log的inode记录;最后从此inode记录的block指针找到a.log的data block。至此,就能读取到/mnt/a.log文件的内容。

下图能更完整的描述上述过程。

33333333333333333333.png

那么又如何读取/mnt/cdrom中的/mnt/cdrom/a.rpm呢?这里cdrom代表的文件系统B挂载点位于/mnt下,所以又多了一个步骤。先找到"/",再找到根中的mnt,进入到mnt文件系统中,找到cdrom的data block,再进入到cdrom找到a.rpm。也就是说,mnt目录文件存放位置是根,cdrom目录文件存放位置是mnt,最后a.rpm存放的位置才是cdrom。

继续完善上图。如下。

444444444444444.png


标题:文件系统中文件操作流程
作者:reyren
地址:https://www.reyren.cn/articles/2021/06/21/1624611158161.html

    评论
    0 评论
avatar

取消