Linux VFS分析(一)




首先介绍VFS的数据结构

VFS抽象数据结构

- 超级块(superblock): 存放文件系统控制信息
- 索引节点(inode):    存放具体文件的一般信息,用来标识存储介质上的文件
- 文件(file):         存放已打开的文件和进程之间交互的信息
- 目录项(dentry):     存放文件的名称和文件的路径 

其他重要数据结构包括

- file_system_type:  用来描述不同的文件系统 
- vfsmount:       描述系统中已经挂载的文件系统信息,用于对超级块和inode进行跟踪


superblock的主要数据结构

作用
s_list 指向下一个superblock,所有的superblock组成一个链表,头指针保存在super_blocks变量中
s_fs_info 指向特定文件系统的superblock信息,比如ext2,这个指针指向ext2_sb_info结构
s_dirt 因为superblock既存在于内存中也存在于磁盘中,所以需要这个标记标示内存中的数据是否和磁盘中的同步
s_root 文件系统根目录的dentry,表示这个文件系统的mount点
s_inodes 指向所有inodes组成的链表的头指针
s_files 指向所有file对象组成的链表的头指针
s_op 超级块操作函数集,struct super_operations类型,包括alloc_inode,destroy_inode,put_super等函数,用来管理这个文件系统中的 inode 的函数
   

inode的主要数据结构

作用
i_dentry 指向本inode的所有dentry对象组成的链表的头指针
i_ino 指向特定文件系统的superblock信息,比如ext2,这个指针指向ext2_sb_info结构
i_nlink inode的硬连接引用计数
i_count 进程对inode的引用计数
i_uid/i_gid 所属用户/组的id
i_atime inode访问时间,类型域有i_mtime和i_ctime
i_mode inode的权限
i_size 文件大小(单位:字节)
i_data 数据地址索引,struct address_space结构类型
i_mapping 内存索引页面,struct address_space结构类型指针,指向i_data(?)
i_blocks 所占的block数
i_bytes 最后一个block使用了的字节数
i_sb_list 指向super_block中s_inodes链表中本inode的下一个inode
i_op 索引节点操作函数集,inode_operations类型,定义直接在 inode 上执行的操作
i_cdev/i_pipe/i_bdev 非特殊文件时,inode所存储的设备
i_rdev 非特殊文件时,所存储设备的设备号
   

dentry的主要数据结构

作用
d_name 文件名
d_inode 指向的inode
d_parent 父目录的dentry对象
d_count 目录项对象使用计数器
d_child 对于目录来说,和本dentry相同父目录的目录dentry对象链表的头指针
d_subdirs 对于目录来说,当前dentry下的子目录dentry对象链表的头指
d_alias 指向相同inode的所有dentry对象构成的链表的头指针
d_op 目录项操作表,dentry_operations类型
d_sb 文件的超级块对象
   


file的主要数据结构

作用
f_dentry 指向当前file对象关联的dentry对象
f_vfsmnt  
f_pos 文件指针
f_op 定义与文件和目录相关的方法(即标准系统调用POSIX标准文件I/O管理函数),file_operations结构类型
   

file_system_type的主要数据结构

作用
name 文件系统的名称
fs_flags 一些特殊标记,如FS_REQUIRES_DEV
mount 本文件系统的挂载方法,执行kern_mount函数时调用
kill_sb 卸载文件系统超级块的方法
next file_system_type类型单项链表,指向其他文件系统,单链表的表头由全局变量file_systems表示
fs_supers 双向链表,指向本文件系统所支持的super_block
   

vfsmount的主要数据结构

作用
mnt_parent 上一层挂载点
mnt_mounts 子文件系统的链表头
mnt_child 指向 上一层挂载点mnt_mounts形成双链表 的兄弟结点
mnt_mountpoint 指向挂载点dentry结构的指针
mnt_root 指向本文件系统的根路径dentry
mnt_sb 指向文件系统超级块
mnt_flags 挂载选项,如MNT_NODEV,MNT_READONLY等
mnt_list 指向vfsmount结构所形成链表的头指针(?)
   


tip:通常,superblock, inode会保存至存储介质,而dentry ,file仅存在于内存中

2.文件系统初始化过程 

对于特定的文件系统,  其初始化过程主要是

static struct vfsmount *fixfs_mnt;
static struct file_system_type fixfs_fs_type = {
.owner = THIS_MODULE,
.name = "fixfs",
.mount = fixfs_mount,
.kill_sb = fixfs_kill_sb,
};

static int __init init_fixfs_fs(void)
{
    int ret;

    /* fixfs specify init */
    ret = register_filesystem(&fixfs_fs_type);
    if (!ret) {
fixfs_mnt = kern_mount(&fixfs_fs_type);
if (IS_ERR(fixfs_mnt)) {
            fixfs_mnt = NULL;
            unregister_filesystem(&fixfs_fs_type);
}
}
    /* error handle specs */
}

static void __exit exit_fixfs_fs(void)
{
    unregister_filesystem(&fixfs_fs_type);

    /* fixfs specify cleanup */
}

module_init(init_fixfs_fs);
module_exit(exit_fixfs_fs);

需要定义的数据结构包括file_system_type,super_operations等 
register_filesystem  完成文件系统的注册,即将该fixfs_fs_type挂到全局链表file_systems上 
kern_mount  调用fixfs_fs_type实现的fixfs_mount方法将文件系统挂载至fixfs_mnt供内核使用 
fixfs_mount 通过sget来动态创建超级块sb并挂载至全局链表super_blocks,然后调用相关函数或fill_super完成sb的初始化

tip:对于特点的文件系统一般只进行注册,kern_mount这步是不存在的,挂载过程由系统调用mount来实现(?)

 
3.Linux特殊文件系统 

Linux特殊文件系统包括rootfs, proc, sysfs, swap, tmpfs等pseudo filesystems 
它们都是基于内存的文件系统,有其特殊的用途

- rootfs初始化

vfs_caches_init() –> mnt_init() –> init_rootfs(),init_mount_tree() 
这两个函数创建一个虚拟的根文件系统(基于内存),后面会指向真正的根文件系统(基于block_dev)

static struct file_system_type rootfs_fs_type = {

    .name        = "rootfs",
    .mount        = rootfs_mount,
    .kill_sb    = kill_litter_super,
};
int __init init_rootfs(void)
{
    int err;
    err = bdi_init(&ramfs_backing_dev_info);
    if (err)
        return err;
    err = register_filesystem(&rootfs_fs_type); /* 注册rootfs */
    if (err)
        bdi_destroy(&ramfs_backing_dev_info);
    return err;
}

 

static void __init init_mount_tree(void)

{
    struct vfsmount *mnt;
    struct mnt_namespace *ns;
    struct path root;

    mnt = do_kern_mount("rootfs", 0, "rootfs", NULL); /* 挂载rootfs */
    if (IS_ERR(mnt))
        panic("Can't create rootfs");

    ns = create_mnt_ns(mnt);
    if (IS_ERR(ns))
        panic("Can't allocate initial namespace");

    init_task.nsproxy->mnt_ns = ns; /* 将rootfs的mnt和dentry信息记录到init_task的fs结构中 */
    get_mnt_ns(ns);

    root.mnt = mnt;
    root.dentry = mnt->mnt_root;

    set_fs_pwd(current->fs, &root);
    set_fs_root(current->fs, &root); /* 将rootfs设置为根文件系统 */
}

上面两个函数执行完成后,rootfs的挂载点默认为"/",init_task进程的根目录和当前目录为"/"。

以上只是完成初始化,下面介绍实际的根文件系统挂载 
在Linux中,允许实际的根文件系统存放在不同的地方,如硬盘分区,软盘,NFS或保存在ramdisk中,此时需要在内核中指定变量ROOT_DEV或由bootloader来传递root提供包含根文件系统的设备号 
也可以将实际的根文件系统编进内核

实际根文件系统的挂载在函数prepare_namespace中,该函数必须在do_basic_setup之后 
之所以在do_basic_setup之后,因为do_basic_setup完成了模块的加载(do_initcalls),也包括设备驱动程序,这样后面在具体设备上读取根文件系统才能成功。

在do_initcalls函数会执行rootfs_initcall, 
对于initrd类型(initrd机制)会调用populate_rootfs,否则调用default_rootfs 
populate_rootfs主要完成initrd的检测工作,检查并解压CPIO Initrd或Initramfs或Image-Initrd 
对于initramfs和cpio-initrd的,都会将文件系统解压到根文件系统,此时将不会执行prepare_namespace 
default_rootfs创建目录(/dev,/root)及节点(/dev/console)

prepare_namespace首先会解析内核参数root(由bootloader传递)并生成ROOT_DEV 
然后尝试加载Image-Initrd(/initrd.image) 
然后会尝试加载软盘0分区上的根文件系统 
最后通过mount_root依次加载NFS,软盘1分区及块设备上的根文件系统 
当成功加载一种根文件系统后,会进行根文件系统的切换

当实际根文件系统成功挂载后,init_post会执行一些清理工作并执行init程序切换至用户空间,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值