1. 程式人生 > >Linux檔案子系統( VFS)的初始化

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初始化。