Last Update:2010/11/27
本文涉及VFS中数据结构有:struct file_system_type;struct vfsmount;struct fs_struct;struct files_struct;struct nameidata;struct qstr;
与文件系统相关的数据结构
VFS中使用file_system_type结构来描述一个具体的文件系统类型。也就是说,Linux支持的所有文件系统类型都分别唯一的对应一个file_system_type结构。如果某个具体文件类型的文件系统被安装到了系统中,那么系统就会创建一个vfsmount结构。这个结构用来描述一种文件系统类型的一个安装实例。
struct file_system_type
该结构位于yoursource/include/linux/fs.h中,下面对该结构中部分字段进行说明:
1736struct file_system_type { 1737 const char *name; 1738 int fs_flags; 1739 int (*get_sb) (struct file_system_type *, int, 1740 const char *, void *, struct vfsmount *); 1741 void (*kill_sb) (struct super_block *); 1742 struct module *owner; 1743 struct file_system_type * next; 1744 struct list_head fs_supers; 1745 1746 struct lock_class_key s_lock_key; 1747 struct lock_class_key s_umount_key; 1748 struct lock_class_key s_vfs_rename_key; 1749 1750 struct lock_class_key i_lock_key; 1751 struct lock_class_key i_mutex_key; 1752 struct lock_class_key i_mutex_dir_key; 1753 struct lock_class_key i_alloc_sem_key; 1754};
name:文件系统的名字,不能为空;
get_sb:在安装文件系统时,调用此指针所指函数以在磁盘中获取超级块;
kill_sb:卸载文件文件系统时候,调用此指针所指函数以进行一些清理工作;
owner:如果一个文件系统以模块的形式加载到内核,则该字段用来说明哪个模块拥有这个结构。一般为THIS_MODULE;
next:所有的文件系统类型结构形成一个链表,该链表的头指针为全局变量file_systems(struct file_system_type *file_systems)。这个字段指向链表中下一个文件系统类型结构;
fs_supers:同一个文件系统类型下的所有超级块形成一个双联表,这个字段是这个双联表的头结点。超级块之间通过s_instances字段相互链接;
struct vfsmount
在解释这个结构的相关字段之前,我们很有必要了解一些概念。当我们安装linux时,硬盘上已经有了一个分区安装了ext3(或ext4)文件系统,这个文件系统被称之为根文件系统。系统安装完毕后,如果要继续安装其他文件系统,就需要挂载(mount)。具体的,将要安装的文件系统的根目录挂载到根文件系统的某个子目录上。这样,新安装的文件系统的根目录就是父文件系统下的某个子目录,我们将这个子目录称为安装点(mount point)。
该结构存储在yoursource/include/linux/mount.h中,下面对该结构的部分字段进行说明:
49struct vfsmount { 50 struct list_head mnt_hash; 51 struct vfsmount *mnt_parent; /* fs we are mounted on */ 52 struct dentry *mnt_mountpoint; /* dentry of mountpoint */ 53 struct dentry *mnt_root; /* root of the mounted tree */ 54 struct super_block *mnt_sb; /* pointer to superblock */ 55 struct list_head mnt_mounts; /* list of children, anchored here */ 56 struct list_head mnt_child; /* and going through their mnt_child */ 57 int mnt_flags; 63 const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ 64 struct list_head mnt_list; …… …… 71 int mnt_id; /* mount identifier */ 72 int mnt_group_id; /* peer group identifier */ 78 atomic_t mnt_count; 79 int mnt_expiry_mark; /* true if marked for expiry */ 80 int mnt_pinned; 81 int mnt_ghosts; …… …… 87};
mnt_hash:内核通过哈希表对vfsmount进行管理,当前vfsmount结构通过该字段链入相应哈希值对应的链表当中;
mnt_parent:指向父文件系统对应的vfsmount结构;
mnt_mountpoint:指向该文件系统安装点对应的dentry;
mnt_root:该文件系统对应的设备根目录的dentry;(两者是否指向同一个dentry?mnt_root是指向该文件系统设备根目录的dentry,具体这个dentry是什么?)
mnt_sb:指向该文件系统对应的超级块;
mnt_child:同一个父文件系统中的所有子文件系统通过该字段链接成双联表;
mnt_mounts:该字段是上述子文件系统形成的链表的头结点;
mnt_list:所有已安装的文件系统的vfsmount结构通过该字段链接在一起;
与路径查找有关的辅助结构
我们在使用open系统调用时,给该函数的第一个参数传递的是文件路径名。open函数对文件的操作最终会转化为对该文件inode的操作。VFS为了识别目标文件,会沿着路径逐层查找。因此,VFS中引入了nameidata结构。nameidata结构用于在路径查找过程中记录中间信息和查找结果。该结构体定义在yoursource/include/linux/namei.h中。
18struct nameidata { 19 struct path path; 20 struct qstr last; 21 struct path root; 22 unsigned int flags; 23 int last_type; 24 unsigned depth; 25 char *saved_names[MAX_NESTED_LINKS + 1]; 26 27 /* Intent data */ 28 union { 29 struct open_intent open; 30 } intent; 31};
33struct qstr { 34 unsigned int hash; 35 unsigned int len; 36 const unsigned char *name; 37};
与进程有关的数据结构
struct fs_struct
每个进程都有自己的根目录和当前的工作目录,内核使用struct fs_struct来记录这些信息,进程描述符中的fs字段便是指向该进程的fs_struct结构。该结构定义于yoursource/include/linux/fs_struct.h结构中。
6struct fs_struct { 7 int users; 8 spinlock_t lock; 9 int umask; 10 int in_exec; 11 struct path root, pwd; 12};
users:用户数量;
lock:保护该结构的自旋锁;
umask:打开文件时设置的文件访问权限;
paroot:进程的根目录;
pwd:进程的当前工作目录;
struct files_struct
一个进程可能打开多个文件。所有被某个进程已打开的文件使用struct files_struct来记录。进程描述符的files字段便指向该进程的files_struct结构。该结构定义于yoursource/include/linux/fdtable.h中。
44struct files_struct { 48 atomic_t count; 49 struct fdtable *fdt; 50 struct fdtable fdtab; 54 spinlock_t file_lock ____cacheline_aligned_in_smp; 55 int next_fd; 56 struct embedded_fd_set close_on_exec_init; 57 struct embedded_fd_set open_fds_init; 58 struct file * fd_array[NR_OPEN_DEFAULT]; 59};
count:引用计数;
fdtab:内核专门使用fdtable结构(该结构也称为文件描述符表)来描述文件描述符。该字段为初始的文件描述符表;
fdt:指向fdtab描述符表;
next_fd:最近关闭的文件描述符中数值最小的下一个可用的文件描述符;
close_on_exec_init:执行exec()时需要关闭的文件描述符;
open_fds_init:当前已经打开的文件描述符;
fd_array[NR_OPEN_DEFAULT]:文件对象的初始化数组;
我们都知道open函数正确执行后会返回一个整形的文件描述符,其实这个整数便是fd_arrary数组的下标。我们所说的标准输入文件、标准输出文件和标准错误文件分别是这个数组的0、1和2号元素。
通常在x86体系架构中NR_OPEN_DEFAULT的大小为32,难道一个进程最多只可以打开32个文件吗?当然不是,我们在上述的字段描述中,对fdtab和fd_array都用“初始化”来修饰。也就是说,当一个进程打开的文件数目超过32的时候,内核就分配一个更大的文件对象指针数组(fd_array中的文件对象指针也会被“复制”新的数组中),以便可以存放更多打开文件所对应的file结构体的指针。
上述字段已经说过,内核通过专门的struct fdtable来描述文件描述符表。该结构定义在yoursource/include/linux/file.h中。
32struct fdtable { 33 unsigned int max_fds; 34 struct file ** fd; /* current fd array */ 35 fd_set *close_on_exec; 36 fd_set *open_fds; 37 struct rcu_head rcu; 38 struct fdtable *next; 39};
max_fds:当前文件对象数组中元素的个数;
fd:指向当前文件对象数组,初始指向fd_arrary;
close_on_exec:执行exec时要关闭的文件描述符,初始指向close_on_exec_init;
open_fds:当前已经打开的文件描述符,初始指向open_fds_init;