_Linux中的VFS实现 [一]

d33c4045-7913-eb11-8da9-e4434bdf6706.png

简约而不简单

在Linux中,不管是访问什么文件,也不管使用的是什么文件系统,基本都可以统一地使用诸如open(), read()和write()这样的接口。表面上看这些接口都很简单,但要基于不同的存储介质,适应不同的文件系统,并不是件容易的事。

d53c4045-7913-eb11-8da9-e4434bdf6706.png

这得归功于VFS(Virtual Filesystem),它提供的抽象让不同的文件系统表现出一致的行为。对用户空间和内核的其他部分,这些文件系统看起来都是一样的(比如都有文件和目录,都支持添加和删除操作),不用关注不同文件系统的具体细节。

d63c4045-7913-eb11-8da9-e4434bdf6706.png

如何实现抽象

VFS基于Unix文件系统模型,其提供的抽象与上文的描述基本一致。除了表示一个文件的"inode",以及管理一个文件系统的"superblock"外,还有一个关于路径的抽象"dentry"。

关于一个文件的inode信息,可通过"stat"命令获取:

d83c4045-7913-eb11-8da9-e4434bdf6706.png

我们可以对照着上面这张图,来看下Linux的inode数据结构中与之有关的部分。

访问控制

每个文件有三种与之关联的权限,分别是读、写和执行。试图访问文件的用户也划分为三类,分别是文件的所有者(user)、与所有者在同一用户组的用户(group),以及其他用户(others)。

d93c4045-7913-eb11-8da9-e4434bdf6706.png

可通过"chmod"命令修改文件的权限,通过"chown"命令修改文件的UID和GID。

struct 

每个文件都有三种timestamp:文件上次被访问的时间(access time,简称atime)、文件上次被修改的时间(modification time,简称mtime)和文件属性上次被修改的时间(change time,简称ctime)。

struct 

mtime针对的是文件的内容(即user data),而ctime针对的是inode结构自身(即meta data)。

db3c4045-7913-eb11-8da9-e4434bdf6706.png
特殊文件

上面示例的这个文件是regular file,此外,设备在Linux中也被视作文件,一个设备可以是block device(即"i_bdev"),也可以是character device(即"i_cdev"),而且设备还具有主设备号和从设备号(即"i_rdev")。

dev_t 

如上文所说,目录(directory)也被视作一种特殊的文件,因而它没有独立的数据结构,且基于文件的大部分操作也可用于directory。

dc3c4045-7913-eb11-8da9-e4434bdf6706.png
inode编号和superblock

一个文件必然处于一个文件系统中,因而一个inode也必然被一个superblock所管理(由"i_sb"指向)。同一superblock的所有inodes以双向链表的形式连接(即"i_sb_list"),每个inode在其所属的superblock中有唯一的编号(即"i_ino",对应上面stat命令输出的"Inode"项)。

struct 
两种link

一个文件可以有两种link:hard linksymbolic/soft link,可分别通过"ln"和"ln -s"命令创建。

de3c4045-7913-eb11-8da9-e4434bdf6706.png

用"cat"命令查看文件包含的内容,得到的输出都是一样的:

df3c4045-7913-eb11-8da9-e4434bdf6706.png

但是它们的inode编号却不尽相同(通过"ls -i"查看),hard link与原文件的inode号相同,soft link则有单独的inode号。

e03c4045-7913-eb11-8da9-e4434bdf6706.png

再来查看文件的详细信息:

e13c4045-7913-eb11-8da9-e4434bdf6706.png

输出的第一列代表user, group和others的访问权限,第三列是文件的大小(至于第二列的含义,将在后面给出。)。原文件和hard link的大小都是6个字节,也就是"hello"字符串的大小,soft link的大小则是4个字节。

现在用"rm"命令删除原文件,并通过strace工具追踪这期间发生的系统调用。

e43c4045-7913-eb11-8da9-e4434bdf6706.png

可以看到,它调用了"unlink",为什么不是叫"remove"或者"delete"呢?先来试试删除原文件之后,还能否继续使用hard link和soft link。

e53c4045-7913-eb11-8da9-e4434bdf6706.png

hard link还可以正常访问原来的内容,而soft link的访问则会失败。这一切的原因还得从hard link和soft link的属性说起。

当创建一个文件时,我们需要选择一个路径(pathname),并为文件设置一个字符串形式的名称(symbol)。这其实做了两件事,一是生成一个inode结构体,用于记录这个文件的所有相关信息,包括大小、在磁盘上占据的blocks数目等,二是将生成的inode关联(link)了这个路径和名称。

一个文件的hard link增加的是对这个inode结构体的关联/指向,并不是一个新的文件。而soft link本身就是一个文件,就像directory这种特殊文件里存放的是该目录下包含哪些文件,soft link这种文件里存放的则是指向原inode的路径,路径越长,soft link的大小就越大。这就是为什么hard link和原文件的inode号相同,而soft link不同。

e63c4045-7913-eb11-8da9-e4434bdf6706.png

因此,当我们用"rm"命令“删除”原文件时,删除的只是原文件的路径和inode之间的关联,而不是这个inode本身,文件的内容依然存在于磁盘中,因而只能算是"unlink"。所以直接关联inode的hard link不受影响,而关联原文件路径的soft link此时相当于是一个dangling reference。

一个inode被link的数目由"i_nlink"表示(这就是前面"ls -l"命令输出中第二列数值的含义):

unsigned 

假设现在有这样的一个目录结构:

e83c4045-7913-eb11-8da9-e4434bdf6706.png

来看一下其中的每个directory和每个regular file的link数目:

e93c4045-7913-eb11-8da9-e4434bdf6706.png

每个目录下,除了包含文件和子目录,还包含一个对当前目录的指向(用"."表示)和一个对上级目录/父目录的指向(用".."表示)。

一个普通文本文件(比如file和sub-file)的link数目是1,一个没有子目录的directory(比如sub-test)的link数目是2,包含了自身的引用和父目录对它的引用,一个含有子目录的directory(比如test)的link还包含了子目录对它的指向,所以link数目大于2。

那什么时候,文件的内容才会被真正的从磁盘上消失,对应的inode管理结构也不再存在呢?这将在后续的文章中给出答案。

相比起soft link,hard link在使用的时候有个限制,就是必须和原文件位于相同的文件系统,原因还是和inode编号有关。因为一个inode编号只在文件所属的superblock中是唯一的,而hard link使用和原文件相同的inode编号,如果hard link跑到其他文件系统,就可能和这些文件系统中既有的文件inode编号冲突。

下文将围绕一个文件的打开、读写和关闭等基础操作,进一步讲解VFS中各个抽象元素之间的交互。

参考:

《Red Hat Linux 用户基础》第5章

原创文章,转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值