Linux文件子系统( VFS)的初始化

目录

 

概述

文件的描述

VFS系统的数据类型

各个数据结构之间的关系

VFS初始化


概述

Linux世界中一切都是文件,Linux文件子系统VFS(Virtual Filesystem)为用户提供了文件和文件系统的相关接口,系统中所有文件系统依赖VFS来协同工作。Linux的 文件包含两部分目录和文件,一般采用树状的结构进行组织,如:

│   ├── Desktop
│   │   └── JProfiler.desktop
│   ├── develop
│   │   ├── busybox
│   │   │   ├── applets
│   │   │   │   ├── applets.c
│   │   │   │   ├── applets.o
│   │   │   │   ├── applet_tables
│   │   │   │   ├── applet_tables.c
│   │   │   │   ├── built-in.o

路径字符串:
/home/dw/readme.txt 
表示一个名称为readme.txt 的文件,它在目录/home/dw中。在这里目录和文件看起来属于不同的实例,但首先明确一下,在linux中目录也属于文件,它可以列出其中所包含的所有文件,它也可以执行文件操作。

文件的描述

文件的组成:
    1. 文件
        1. 文件的相关信息(创建时间,拥有者等也称为元数据inode)
        2. 文件本身
    2. 文件系统的控制信息
VFS为了描述为了描述文件,在VFS系统中使用了对应的四种对象:
    • 超级块对象
    • 索引节点对象
    • 目录对象
    • 文件对象
其中在大多linux系统中超级块对象会保存在磁盘的特定扇区否则会在使用现场创建

VFS系统的数据类型

数据类型

描述

struct file_system_type

描述文件系统类型,如ext4

struct super_block

每种文件系统都必须实现超级块对象,用于存储特性文件系统信息,对应存放与磁盘特定扇区

struct inode

包含内核操作的全部信息,对于linux可以从磁盘索引节点直接读入,否则要文件系统提取这些信息,在内存中创建,便于文件系统使用。

struct dentry

文件包括路径中的每个部分都是目录项,没有对应的磁盘数据结构。有三种状态:1)被使用2)未被使用3)无效目录项

struct mount

安装文件系统的实例,代表一个安装点,包含一个 struct vfsmount成员

struct file

使用进程的观点看文件,表示进程已经打开的文件。包含访问模式,当前偏移等。

struct fs_struct

进程描述符中使用,文件系统和进程相关信息,如当前工作目录和根目录

struct files_struct

进程描述符中使用,进程的相关信息,如打开的文件以及描述符

struct nsproxy

进程描述符中使用,进程的命名空间层次结构。默认情况下,所有的进程共享相同的命名空间,可以通过CLONE_NEWNS配置。

int copy_namespaces(unsigned long flags, struct task_struct *tsk)

{

    ………………..

    if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |

                CLONE_NEWPID | CLONE_NEWNET)))) {

        get_nsproxy(old_ns);

        return 0;

    }

    ………………..

}

各个数据结构之间的关系


各个数据结构之间的引用关系:

VFS初始化


在start_kernel中通過下面兩個函數初始化:

asmlinkage __visible void __init start_kernel(void)
{
    …….
    vfs_caches_init_early();
    …….
    vfs_caches_init(totalram_pages);
    …….
}

其中:

void __init vfs_caches_init_early(void)
{
    dcache_init_early(); //创建2个表 1:slab cach和2:一个hash表
    inode_init_early();  //创建2个表 1:slab cach和2:一个hash表
}

void __init vfs_caches_init(unsigned long mempages)
{
    unsigned long reserve;

    /* Base hash sizes on available memory, with a reserve equal to
           150% of current kernel size */

    reserve = min((mempages - nr_free_pages()) * 3/2, mempages - 1);
    mempages -= reserve;

    names_cachep = kmem_cache_create("names_cache", PATH_MAX, 0,
            SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);

    dcache_init();
    inode_init();  
    files_init(mempages);  /*创建struct file的slab cache*/
    //至此,创建了struct dentry, struct inode的slab cache和hash table,struct file  的slab cache,一共5个cache。

    mnt_init();
    bdev_cache_init();
    chrdev_init();
}

在dcache_init,inode_init同dcache_init_early, inode_init_early函数中分别创建dentry和inode的slab cache和hash table,在slab cache中保存数据,使用hash table为其建立索引表,典型的以空间换时间方式。这里使用有early后缀和没有early后缀的函数是根据hash是否分布在NUMA上来选择hash table的创建时机是否推迟到vmalloc空间可以使用。

void __init inode_init(void)
{
    unsigned int loop;

    /* 创建inode slab cache */
    inode_cachep = kmem_cache_create("inode_cache",
                     sizeof(struct inode),
                     0,
                     (SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
                     SLAB_MEM_SPREAD),
                     init_once);

    ........

        /* 创建inode hash table*/
    inode_hashtable =
        alloc_large_system_hash("Inode-cache",
                    sizeof(struct hlist_head),
                    ihash_entries,
                    14,
                    0,
                    &i_hash_shift,
                    &i_hash_mask,
                    0,
                    0);

    for (loop = 0; loop < (1U << i_hash_shift); loop++)
        INIT_HLIST_HEAD(&inode_hashtable[loop]);
}

在调用sysfs_init函数之前为dentry, inode,file,mount,kernfs_node创建了slab cache,为了方便索引、提高效率还创建了部分hash表。

void __init mnt_init(void)
{
    unsigned u;
    int err;

    /*创建struct mount 的slab cache,通过alloc_vfsmnt函数进行分配*/
    mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount),
            0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);

    mount_hashtable = alloc_large_system_hash("Mount-cache",
                sizeof(struct hlist_head),
                mhash_entries, 19,
                0,
                &m_hash_shift, &m_hash_mask, 0, 0);
    mountpoint_hashtable = alloc_large_system_hash("Mountpoint-cache",
                sizeof(struct hlist_head),
                mphash_entries, 19,
                0,
                &mp_hash_shift, &mp_hash_mask, 0, 0);
       
    if (!mount_hashtable || !mountpoint_hashtable)
        panic("Failed to allocate mount hash table\n");

    for (u = 0; u <= m_hash_mask; u++)
        INIT_HLIST_HEAD(&mount_hashtable[u]);
    for (u = 0; u <= mp_hash_mask; u++)
        INIT_HLIST_HEAD(&mountpoint_hashtable[u]);

    /*创建名为"kernfs_node_cache"的struct kernfs_node结构体的slab cache*/
    kernfs_init(); 

    err = sysfs_init();   //创建struct kernfs_root sysfs_root,注册sysfs文件系统
    if (err)
        printk(KERN_WARNING "%s: sysfs_init error: %d\n",
            __func__, err);
    fs_kobj = kobject_create_and_add("fs", NULL);
    if (!fs_kobj)
        printk(KERN_WARNING "%s: kobj create error\n", __func__);

    //注册文件系统:rootfs和ramfs
    init_rootfs(); 
    init_mount_tree();
}

在上面函数中注册了3个文件系统类型(file_system_type),分别是:

sysfs : sysfs_fs_type
rootfs: rootfs_fs_type
ramfs : ramfs_fs_type

下面通过init_mount_tree 函数安装根文件系统  :其中,文件系统的安装和初始化需要分配super_block和inode,他们的分配初始化通过下面的调用完成。

init_mount_tree-->vfs_kern_mount-->
mount_fs-->rootfs_mount-->mount_nodev-->ramfs_fill_super。

error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);

static void __init init_mount_tree(void)
{
    struct vfsmount *mnt;
    struct mnt_namespace *ns;
    struct path root;
    struct file_system_type *type;

    type = get_fs_type("rootfs");  //查找rootfs_fs_type
    if (!type)
        panic("Can't find rootfs type");

    // 1). 分配struct mount  2).安装文件系统
    mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
    put_filesystem(type);
    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;
    get_mnt_ns(ns);

    root.mnt = mnt;
    root.dentry = mnt->mnt_root;
    mnt->mnt_flags |= MNT_LOCKED;

    // 设置当前进程init_task的当前目录和根目录。
    set_fs_pwd(current->fs, &root);
    set_fs_root(current->fs, &root);
}

随后vfs_caches_init调用bdev_cache_init、chrdev_init。

void __init vfs_caches_init(unsigned long mempages)
{
    ......
    mnt_init();
    bdev_cache_init();
    chrdev_init();
}

初始字符设备,块设备,完成VFS初始化。

 

猜你喜欢

转载自blog.csdn.net/zhoudawei/article/details/85934072