前言
学习《深入linux内核架构》的书本阅读。基本是书中知识点的摘抄。
VFS虚拟文件系统
VFS在内核的层次结构图。
应用目的:为支持本机文件系统,同时允许访问其他操作系统的文件,Linux内核在用户层和文件系统间引入了抽象层,虚拟文件系统(Virtual File System)。
文件系统类型
- Disk-based Filesystem
如EXT2 - Virtual Filesystem
如proc - 网络文件系统
如NFS
通用文件模型
VFS的解决方案,提供一种结构模型,包含文件系统所应具备的所有组件。此模型,只存在与虚拟中,必须使用各种对象和函数指针与每种文件系统适配。
处理文件时,内核空间和用户空间使用的主要对象是不同的。
用户程序:一个文件由一个文件描述符标识,描述符是整数,用作所有有关文件的操作中用作标识文件的参数。文件描述符是在打开文件时由内核分配的,只在一个进程内部有效。(这很重点,同桌还说是否有可能功用一个文件描述符,在这里看起来就不是了)。两个不同的进程可以使用同样的文件描述符,但是二者不能指向同一个文件。基于同一个描述符来共享文件是不可能的(为什么呢?)
内核空间: 内核处理文件的关键是inode
。每个文件(和目录)都有且仅有一个对应的inode
,其中包含元数据(访问时间,等),和指向文件数据的指针。但inode
不包含文件名。
inode
先给出inode在内核中的结构体定义。
inode
成员分为两类:
- 描述文件状态的元数据。如,
atime
,ctime
,mtime
- 保存实际文件内容的数据段(或者指向数据的指针)
书中给了一个示例,来阐述利用inodes
来构造文件系统的目录层次结构。
(看起来是结合了inode
, dentry
来完成文件的查找,只是当前还没有介绍dentry
的概念)
查找首先,起始于一个inode
,表示根目录/
。(根目录的inode
,对于系统来说是已知的)。根目录是由一个inode
表示,其数据段并不包含普通的数据,而是根目录下的各个目录项。
目录项由两个成员组成:
- 该目录项的数据所在
inode
的编号 - 文件或者目录的名称
系统中所有的inode
都有一个特定的编号,用于唯一标识各个inode
。文件名和inode
之间的关联就是通过该编号完成的。
以/usr/bin/emacs
为例,查找过程:
第一步,查找子目录usr
的inode
,这一步会扫描根inode
的数据段,直至找到一个名为usr
的目录项。
第二步,重复第一步,直至在bin
的数据段中查找到emacs
的目录项,这时仍旧返回一个inode
编号而非目录。(这很奇怪呀,如果最后一项仍旧是目录呢?)
第三步,最后一个inode
的文件内容,与前三个inode
不同。前三个inode
都表示目录,其文件内容是目录项的列表,包括子目录和文件。与emacs
文件关联的inode
,其数据段存储了文件的内容。
下面,给出一张图片,完整描述上面的故事:
链接
链接(link
)是建立文件系统对象之间的联系。
软连接:
又称符号链接,(可以理解为方向指针,或者是快捷方式),表示文件存在于某一个特定位置。常用的场景,例如若想自己编译的可执行文件能够全局可用,就可以使用:
[root@k8s-master Workspace]# ln -s /root/Workspace/exec /usr/bin/exec
[root@k8s-master /]# exec
Hello World
本质上,软连接和链接目标没有紧密的耦合,软连接可以认为是一个目录项,除了指向文件名的指针,并不存放其他数据。。目标文件与链接文件均有独立的inode,相应的inode
的数据端包含一个字符串,给出了链接目标的路径。目标文件删除,符号链接仍旧可以保持。
1. 软连接, inode值不同
$ ln -s [root@192 shell]# ln -s /root/Code/homework/shell/exec /usr/bin/exec
[root@192 shell]# ls -i exec
104907757 exec
[root@192 shell]# ls -i /usr/bin/exec
5039594 /usr/bin/exec
2. 删除软连接
$ rm -f /usr/bin/exec
硬链接:
硬链接建立时,创建的目录项使用了一个现存的inode
编号,即自身。
3. 硬连接,inode值相同
[root@192 shell]# ln /root/Code/homework/shell/exec /usr/bin/exec
[root@192 shell]# ls -i exec
104907757 exec
[root@192 shell]# ls -i /usr/bin/exec
104907757 /usr/bin/exec
4. 删除硬连接
rm -f /usr/bin/exec
知识点2:
假定硬连接(B)与原始文件(A)共同享用同一个inode
。一个用户若想要删除文件A,通常会**销毁相关的inode和其数据段,以便释放存储空间以便后续使用。**但将会导致,文件B无法访问。
为此,会在inode
中加入计数器,每对文件创建一个硬连接时,都会将计数器加1.如果其中一个硬连接或者原始文件被删除,那么计数器将减一,计数器归0时,才确认inode
不会再被使用,可以从文件中删除。
编程接口
文件被使用前,必须使用open
和openat
系统调用。在成功打开文件之后,内核向用户层返回一个非负整数。(这里赶脚是文件描述符)。分配的文件描述符起始于3。(这是因为0,1,2分别对应着标准输入,标准输出,标准错误流)。
文件被打开后,名称就没有特别大的用处了,现在仅有文件描述符唯一标识。所有其他的库函数,都需要传递一个文件描述符号作为一个参数。由于多个命名空间和容器的引入(影响啥了呢),具有相同数值的多个文件描述符可以共存于内核中,对文件的唯一标识由一个特殊的数据结构(struct file)提供
将文件作为通用接口
使用文件作为其主要通信手段的一部分内核子系统:
- 字符和块设备
- 进程间的管道
- 用于所有网络协议的套接字(怪不得说,套接字是一个文件呢)
- 用于交互式输入和输出的终端