Linux IO子系统由上至下,依次由VFS虚拟文件系统层,pagecache,Mapping Layer(具体的文件系统),通用块层,IO调度层,块设备驱动层组成。
VFS说明。
VFS虚拟文件系统
VFS建立了文件系统的统一模型,对文件系统的公共属性做了抽象和定义,封装了文件系统的统一接口,应用程序可以不加修改的,透明的访问操作系统上不同文件系统的文件。
VFS层有几个最重要的概念:file、inode、dentry。
File
文件本质上是单纯的字节流,应用程序告诉内核读取文件偏移和长度,内核返回字节流,应用程序自行解读数据内容。
每当打开一个文件的时候,内核为之分配一个文件对象,VFS用struct file来描述它。
<pre name="code" class="html">struct file {
struct path f_path;
<strong>const struct file_operations *f_op; //文件操作</strong>
spinlock_t f_lock;
int f_sb_list_cpu;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
loff_t f_pos; //文件读写位置
}
每一个文件对象都包含打开、关闭、读、写等一系列文件操作的函数指针,不同文件系统注册不同的函数。以ext4文件系统为例,通过crash工具分析一个dump文件,可见
crash> struct file_operations 0xffffffff8162e1c0 //一个位于ext4文件系统上的文件关联的file_operations
struct file_operations {
owner = 0x0,
llseek = 0xffffffff811dd120 <ext4_llseek>,
read = 0xffffffff8114b140 <do_sync_read>,
write = 0xffffffff8114b050 <do_sync_write>,
aio_read = 0xffffffff810f25e0 <generic_file_aio_read>,
aio_write = 0xffffffff811dd2b0 <ext4_file_write>,
mmap = 0xffffffff811dd0d0 <ext4_file_mmap>,
open = 0xffffffff811dcf10 <ext4_file_open>,
release = 0xffffffff811dce50 <ext4_release_file>,
}
每个文件系统不一定实现file_operation中的全部方法,比如proc文件系统,是一个伪文件系统,不实现的方法,以file对象中以null来初始化。
在应用层,文件是以文件描述符来表示的,即FD(file description)俗称句柄。文件file和文件描述符fd是属于进程的,每个进程都有有一个独立的打开的文件列表,可
在/proc/<pid>/fd/目录下查询。
# ls -l /proc/17590/fd/
total 0
dr-x------ 2 root root 0 Jan 4 15:17 ./
dr-xr-xr-x 8 root root 0 Jan 4 15:16 ../
lrwx------ 1 root root 64 Jan 4 15:17 0 -> /dev/pts/1
lrwx------ 1 root root 64 Jan 4 15:17 1 -> /dev/pts/1
lrwx------ 1 root root 64 Jan 4 15:17 2 -> /dev/pts/1
lrwx------ 1 root root 64 Jan 4 15:17 3 -> /dev/null
lrwx------ 1 root root 64 Jan 4 15:17 4 -> /dev/mem
lr-x------ 1 root root 64 Jan 4 15:17 5 -> /usr/src/linux-3.1.10-1.9/vmlinux*
inode
inode是文件的元数据信息。元数据是描述文件本身的数据,如大小,时间戳,大小,属主,访问权限,文件在文件系统的实际位置等等。用stat <file>命令查看到文件的元数据信息。
VFS用inode来描述一个文件的元数据。Inode与文件实体一一对应。多个进程在用户态可以打开同一个文件,在用户态有多个fd对应同一个文件对象,但在内核态中,只存在一个与此文件对应的inode。VFS对文件元数据的操作函数指针保存在struct inode内,仍以ext4为例
crash>struct inode |grep operations
const struct inode_operations *i_op;
crash> struct inode_operations0xffffffff8162e0c0
struct inode_operations {
…
setattr = 0xffffffff811e5190<ext4_setattr>, //ext4设置文件属性方法
getattr = 0xffffffff811e39e0<ext4_getattr>, //ext4获取文件属性方法
…
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
Dentry
在linux中,目录本质上也是一个文件。目录文件的文件内容就是指向其它文件或目录的目录项。
VFS使用struct dentry来描述一个目录项。
构成文件路径上的每一个节点都有一个dentry与之对应,包括最末端的文件节点。
dentry的最重要的一个字段就是名字(d_name), 也就是该目录项对应的文件或目录的名字——文件的名字并不保存在这个文件的inode里面,而是保存在它所在的目录里面。可以说文件名是目录这个特殊文件的主要内容。
看一个实例:
/a/d.txt
“/”对应一个dentry, 其d_name是“/”, 这个是万物之起源,我们可以认为是天然存在的。
在“/”目录内有指向很多目录和文件的目录项,其中一个的d_name是“a”。
在“a”目录内有指向很多目录和文件的目录项,其中一个的d_name是“d.txt”。
如果两个不同的目录项指向同一个文件,是什么情况?这种情况就是文件链接(hard link),即我可以通过两个不同的路径访问同一个文件。这种情况下,该文件对应的inode仍然只有一个,但dentry会有多个。
同时联想到符号链接(soft link)的情况,在这种情况下,链接文件本身是一个独立的文件,有自己的inode,只不过这个文件被定义为特殊类型(symbolic link),这种类型文件的内容就是对其他文件的指向信息。对这些文件的读写等操作,VFS获取到它所指向的文件,然后把操作都映射过去。
可以说整个文件系统层都围绕这三个对象干活儿,所以搞清楚它们的含义至关重要。