天天看點

kernel啟動流程-start_kernel的執行_6.vfs_caches_init1. 前言2.vfs_caches_init3. 總結

目錄

  • 1. 前言
  • 2.vfs_caches_init
    • |- -kmem_cache_create_usercopy("names_cache"...)
    • |- -dcache_init
    • |- -inode_init
    • |- -files_init
    • |- -files_maxfiles_init
    • |- -mnt_init
    • |- -bdev_cache_init
    • |- -chrdev_init
  • 3. 總結

1. 前言

本專題文章承接之前《kernel啟動流程_head.S的執行》專題文章,我們知道在head.S執行過程中儲存了bootloader傳遞的啟動參數、啟動模式以及FDT位址等,建立了核心空間的頁表,最後為init程序初始化好了堆棧,并跳轉到start_kernel執行。

本文重點介紹start_kernel的vfs_caches_init的主要流程.

kernel版本:5.10

平台:arm64

2.vfs_caches_init

vfs_caches_init
    |--names_cachep = kmem_cache_create_usercopy("names_cache"...)
    |--dcache_init()
    |--inode_init()
    |--files_init()
    |--files_maxfiles_init()
    |--mnt_init()
    |--bdev_cache_init()
    \--chrdev_init()
           

|- -kmem_cache_create_usercopy(“names_cache”…)

建立names_cache slab高速緩存描述符?

|- -dcache_init

dcache_init
    |--dentry_cache = KMEM_CACHE_USERCOPY(dentry,...)
    \--dentry_hashtable = alloc_large_system_hash("Dentry cache"...)
           

為dentry建立slab緩存,并且初始化了dentry hash表,後面配置設定的dentry結構都會挂到該hash 表上。dentry_hashtable用來友善虛拟檔案系統vfs快速索引dentry,通過d_hash獲得dentry在dentry_hashtable上所在hash桶的位置,進一步根據dentry的name找到該dentry

|- -inode_init

inode_init
  |--inode_cachep = kmem_cache_create("inode_cache",sizeof(struct inode)...)
  \--inode_hashtable=alloc_large_system_hash("Inode-cache",sizeof(struct hlist_head)...)
           

為inode建立slab緩存,并且初始化了inode hash表,後面配置設定的inode結構都會通過fs/inode.c中的insert_inode_hash函數,将新的配置設定的inode插入到inode_hashtable中。inode_hashtable用來友善虛拟檔案系統vfs快速索引inode,通過ilookup根據ino獲得inode在inode_hashtable上所在hash桶的位置,進一步根據ino找到該inode

|- -files_init

files_init
    |--filp_cachep = kmem_cache_create("filp", sizeof(struct file)
    \--percpu_counter_init(&nr_files, 0, GFP_KERNEL)
           

為filp建立slab緩存;初始化per cpu變量nr_files為0

|- -files_maxfiles_init

from:https://blog.csdn.net/sinat_20184565/article/details/107920497

file-max定義了Linux核心可配置設定的最大數量的檔案句柄,當檢測到檔案句柄耗盡時,需要增加file-max值。

核心函數files_maxfiles_init計算系統預設的最大檔案數量,依據是每個檔案及與其關聯的inode和dcache結構,總的大小約為1K。預設檔案占用的空間不超出記憶體的10%,file-max的最小值為NR_FILE,即8192個

曆史上,核心可以動态的配置設定檔案句柄,但是不能進行釋放。file-nr檔案中的三個值分别表示:配置設定的檔案句柄數量,已配置設定但是未使用的句柄數量,以及最大的檔案句柄數量。在核心版本2.6之後,第二個值總是為零,意味着配置設定的檔案句柄數量與使用的句柄數量完全相同。

/ # cat /proc/sys/fs/file-nr

20160 0 1597700

|- -mnt_init

mnt_init
    |--mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount)...)
    |--mount_hashtable = alloc_large_system_hash("Mount-cache",sizeof(struct hlist_head),...)
    |--mountpoint_hashtable = alloc_large_system_hash("Mountpoint-cache",sizeof(struct hlist_head)...)
    |--kernfs_init()
    |--sysfs_init()
    |--kobject_create_and_add("fs", NULL)
    |--shmem_init()
    |--init_rootfs()
    \--init_mount_tree()
           
  1. kmem_cache_create(“mnt_cache”, sizeof(struct mount)…)

    建立mount高速緩存描述符

  2. alloc_large_system_hash

    初始化mount_hashtable 和mountpoint_hashtable 哈希表。所有的mount資訊都在一個散清單mount_hashtable上維護,所有的mountpoint都在一個散清單mountpoint_hashtable 上維護

  3. kernfs_init

    關于kernfs首先引用一段維基百科的說明:

from: https://en.wikipedia.org/wiki/Kernfs_(Linux)

In the Linux kernel, kernfs is a set of functions that contain the functionality required for creating pseudo file systems used internally by various kernel subsystems. The creation of kernfs resulted from splitting off part of the internal logic used by sysfs, which provides a set of virtual files by exporting information about hardware devices and associated device drivers from the kernel’s device model to user space, into an independent and reusable functionality so other kernel subsystems can implement their own pseudo file systems more easily and consistently.

The associated patchset, with Tejun Heo as its main author, was merged into the Linux kernel mainline in kernel version 3.14, which was released on March 30, 2014.One of the primary users of kernfs is the pseudo file system used internally by cgroups, whose redesign continued into version 3.15 of the Linux kernel.

如上大意說明了kernfs是從sysfs分離出來的,原因是為了核心其它子系統更容易的建立新的僞檔案系統。

kernfs_init
  |--kernfs_node_cache = kmem_cache_create("kernfs_node_cache",sizeof(struct kernfs_node)...)
  \--kernfs_iattrs_cache  = kmem_cache_create("kernfs_iattrs_cache",sizeof(struct kernfs_iattrs)...)
           

kernfs_init首先建立kernfs_node_cache高速緩存描述符。kernfs_node表示kernfs層次的構成部分(building block),每個kernfs節點由單個kernfs_node表示。

  1. sysfs_init
sysfs_init
    |--sysfs_root = kernfs_create_root(NULL...)
    |--sysfs_root_kn = sysfs_root->kn
    \--register_filesystem(&sysfs_fs_type)
           

初始化sysfs檔案系統,主要基于kernfs建立了sysfs_root ,通過register_filesystem完成sysfs的注冊,此處sysfs的注冊位于rootfs之前,說明此時sysfs已經可用。

  1. fs_kobj = kobject_create_and_add(“fs”, NULL)

    建立一個fs的kobject并注冊進sysfs,結果是會産生/sys/fs的目錄

  2. shmem_init

    tmpfs檔案系統初始化

  3. init_rootfs
  4. init_mount_tree
init_mount_tree
    |--mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", NULL)
    |--ns = alloc_mnt_ns(&init_user_ns, false)
    |--初始化mnt和命名空間ns
    |--set_fs_pwd(current->fs, &root)
    \--set_fs_root(current->fs, &root)
           

init_mount_tree完成真正的rootfs的mount動作

(1)vfs_kern_mount

vfs_kern_mount
    |--fs_context_for_mount(type, flags)
    |     \--alloc_fs_context(fs_type, NULL, sb_flags, 0,..)
    |             |--fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL)
    |             |--初始化fc,其中fc->fs_type = get_filesystem(fs_type)
    |             |--init_fs_context = fc->fs_type->init_fs_context
    |             \--init_fs_context(fc)
    |                    \--rootfs_init_fs_context(fc)
    |                           \--ramfs_init_fs_context(fc)
    |--mnt = fc_mount(fc)
           |--vfs_get_tree(fc)
           |      \--fc->ops->get_tree(fc)
           |             \--ramfs_get_tree(fc)
           |                    \--get_tree_nodev(fc, ramfs_fill_super)
           \--vfs_create_mount(fc)
    
           

vfs_kern_mount中為rootfs建立vfsmount.

  • fs_context_for_mount

    alloc_fs_context用來配置設定fs_contex,并對fs_contex進行初始化,其中fc->fs_type = get_filesystem(fs_type),此處的fs_type就是rootfs_fs_type;對于rootfs_fs_type:

init_fs_context = rootfs_init_fs_context
           

是以init_fs_context(fc)會執行rootfs_init_fs_context->ramfs_init_fs_context

ramfs_init_fs_context中最重要的是做了如下的初始化:

fc->ops = &ramfs_context_ops
           
  • fc_mount

    由上步fs_context_for_mount可知fc->ops = &ramfs_context_ops,是以

    fc->ops->get_tree(fc)實際調用的就是ramfs_get_tree,最終将調用到get_tree_nodev(fc, ramfs_fill_super), 他會建立super_block,root dentry, root inode等關鍵資料結構。ramfs_fill_super來填充super_block;

    接着vfs_create_mount會建立出vfsmount,

    ramfs_fill_super這個函數在建構rootfs的mount tree過程中承擔了主要的工作:

    a) ramfs_get_inode來建立root inode

    在這裡我們會對inode->i_op進行指派為:inode->i_op = &ramfs_file_inode_operations

    ramfs_file_inode_operations後續将在rootfs下完成inode的主要操作,如檔案建立、目錄建立等。如在後續cpio initrd釋放的過程中,對于init程式通過調用ramfs_file_inode_operations->create即ramfs_create來建立對應的inode

    b)通過d_make_root來建立root dentry

(2) 初始化mnt和命名空間ns

設定root path的mnt為新建立的mnt;

設定root path的dentry為新建立的root dentry;

這樣就建構起核心的一棵mount tree,之後其它的檔案系統将挂載到這棵mount tree上

(3)設定目前程序(也就是init_task,0号程序,之後會退化為idle程序)的目前目錄pwd為root path,同時将目前程序的根目錄也設定為root path,正因為把目前目錄設為root path,是以在将initrd釋放到rootfs時會釋放到/目錄下。

(4)init_mount_tree小結

init_mount_tree最主要的就是為rootfs建立vfsmount,過程中建立了rootfs的super_block,root dentry, root inode等關鍵資料結構。并通過ramfs_fill_super來填充super_block;

  1. mnt_init小結

    mnt_init主要建立了mount相關的高速緩存描述符,注冊并挂載了先期的幾個檔案系統,包括sysfs, tmpfs, rootfs, 建構出mount tree;關鍵的是為rootfs 建立super_block,root dentry, root inode并填充super_block,為後續其它檔案系統的挂載提供基礎。

|- -bdev_cache_init

bdev_cache_init
    |--bdev_cachep=kmem_cache_create("bdev_cache",sizeof(struct bdev_inode)...)
    |--register_filesystem(&bd_type)
    |--kern_mount(&bd_type)
           |--mnt = vfs_kern_mount(type, SB_KERNMOUNT, type->name, NULL)
           |--real_mount(mnt)->mnt_ns = MNT_NS_INTERNAL
    \--blockdev_superblock = bd_mnt->mnt_sb
           
  1. kmem_cache_create

    建立bdev_cache高速緩存描述符

  2. register_filesystem(&bd_type)

    注冊bd_type檔案系統

  3. kern_mount(&bd_type)

    執行真正的mount操作,會調用vfs_kern_mount函數,執行過程與init_mount_tree->vfs_kern_mount類似

|- -chrdev_init

建立cdev_map用于管理字元裝置号與字元裝置的映射關系

3. 總結

到目前為止已經完成了sysfs, tmpfs, bdevfs的注冊。

vfs_caches_init初始化虛拟檔案系統,主要工作包括:

(1)建立denty,inode,filp的slab高速緩存;

(2)設定支援的最大檔案數量;

(3)通過mnt_init來mount rootfs(真正的根檔案系統,核心挂載的第一個檔案系統);

(4)建立bdev_cache的slab高速緩存并注冊并挂載bdev檔案系統;

(5)建立cdev_map用于管理字元裝置号與字元裝置的映射關系