第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > 【linux kernel】linux内核如何挂载根文件系统

【linux kernel】linux内核如何挂载根文件系统

时间:2022-03-15 07:14:13

相关推荐

【linux kernel】linux内核如何挂载根文件系统

文章目录

一、前世今生二、kernel_init线程入口三、重磅角色—prepare_namespace

一、前世今生

kernel_init线程函数中会调用kernel_init_freeable()函数,在kernel_init_freeable函数中将调用prepare_namespace()函数挂载指定的根文件系统。

【漫漫长路,挂载开始!!!】

二、kernel_init线程入口

kernel_init()函数如下所示(/init/main.c):

static int __ref kernel_init(void *unused){int ret;kernel_init_freeable();async_synchronize_full();free_initmem();mark_rodata_ro();system_state = SYSTEM_RUNNING;numa_default_policy();flush_delayed_fput();if (ramdisk_execute_command) {ret = run_init_process(ramdisk_execute_command);if (!ret)return 0;pr_err("Failed to execute %s (error %d)\n",ramdisk_execute_command, ret);}if (execute_command) {ret = run_init_process(execute_command);if (!ret)return 0;panic("Requested init %s failed (error %d).",execute_command, ret);}if (!try_to_run_init_process("/sbin/init") ||!try_to_run_init_process("/etc/init") ||!try_to_run_init_process("/bin/init") ||!try_to_run_init_process("/bin/sh"))return 0;panic("No working init found. Try passing init= option to kernel. ""See Linux Documentation/init.txt for guidance.");}

上述代码第5行执行完成时,根文件系统就挂载成功了。具体执行函数由prepare_namespace()实现。下文将分析该函数。

第7~8行代码用于释放初始化函数调用时分配的内存,async_synchronize_full()函数用于同步所有的异步函数调用。free_initmem()函数用于释放初始化时所使用的内存空间。

三、重磅角色—prepare_namespace

prepare_namespace函数定义在(/init/do_mounts.c)文件中,如下所示:

void __init prepare_namespace(void){int is_floppy;if (root_delay) {printk(KERN_INFO "Waiting %d sec before mounting root device...\n",root_delay);ssleep(root_delay);}//等待完成对已知设备的探测wait_for_device_probe();md_run_setup();if (saved_root_name[0]) {root_device_name = saved_root_name;if (!strncmp(root_device_name, "mtd", 3) ||!strncmp(root_device_name, "ubi", 3)) {mount_block_root(root_device_name, root_mountflags);goto out;}ROOT_DEV = name_to_dev_t(root_device_name);if (strncmp(root_device_name, "/dev/", 5) == 0)root_device_name += 5;}if (initrd_load()){goto out;}/* 等待所有的异步扫描操作完成 */if ((ROOT_DEV == 0) && root_wait) {printk(KERN_INFO "Waiting for root device %s...\n",saved_root_name);while (driver_probe_done() != 0 ||(ROOT_DEV = name_to_dev_t(saved_root_name)) == 0)msleep(100);async_synchronize_full();}is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR;if (is_floppy && rd_doload && rd_load_disk(0))ROOT_DEV = Root_RAM0;mount_root();out:devtmpfs_mount("dev");sys_mount(".", "/", NULL, MS_MOVE, NULL);sys_chroot(".");}

上述代码本质是三种程序运行方式:

(1)【方式一】:如果root_device_namemtd或者ubi类型的根设备,则调用mount_block_root()函数挂载文件系统。

(2)【方式二】:调用initrd_load()进行早期根文件系统的挂载,如果mount_initrd为true的情况下,将执行根文件系统挂载操作。在linux内核中包含两种挂载早期根文件系统的机制:初始化RAM磁盘(initrd)是一种老式的机制。而initramfs是新的用于挂载早期根文件系统的机制。设计initrdinitramfs机制的目的:用于执行早期的用户空间程序;在挂载真正(最后的)根文件系统之前加载一些必须的设备驱动程序

(3)【方式三】:调用mount_root()函数进行文件系统挂载。该种方式是linux内核中比较常用的方式,在这种方式下,又包含三种文件系统挂载操作:1、nfs方式。2、Floppy方式。3、block方式。在平时开发中,常使用nfs进行网络挂载根文件系统,以便进行开发和调试。

以上三种方式,在实际linux启动过程中,linux内核将选择其中一种作为挂载根文件系统的方式。

下文将继续分析这三种方式:

【方式一】mount_block_root()函数将调用do_mount_root()进行文件系统挂载。如下图所示:

【方式二】initrd方式的文件系统挂载。

【方式三】

对于方式三,会调用mount_root()函数进行根文件系统的挂载,该函数定义如下(/init/do_mounts.c):

void __init mount_root(void){#ifdef CONFIG_ROOT_NFSif (ROOT_DEV == Root_NFS) {if (mount_nfs_root())return;printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");ROOT_DEV = Root_FD0;}#endif#ifdef CONFIG_BLK_DEV_FDif (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {/* rd_doload is 2 for a dual initrd/ramload setup */if (rd_doload==2) {if (rd_load_disk(1)) {ROOT_DEV = Root_RAM1;root_device_name = NULL;}} elsechange_floppy("root floppy");}#endif#ifdef CONFIG_BLOCKcreate_dev("/dev/root", ROOT_DEV);mount_block_root("/dev/root", root_mountflags);#endif}

从以上代码片段可知:在进行mount_root()操作时,同样存在三种方式:

(1)NFS方式

通过mount_nfs_root()函数完成。

(2)ramload方式

通过rd_load_disk()函数完成。

(3)BLOCK方式

通过create_dev()和mount_block_root()两个函数完成。

mount_nfs_root()mount_block_root()两个函数都调用一个核心功能函数:do_mount_root()

该函数定义如下:

static int __init do_mount_root(char *name, char *fs, int flags, void *data){struct super_block *s;int err = sys_mount(name, "/root", fs, flags, data);if (err)return err;sys_chdir("/root");s = current->fs->pwd.dentry->d_sb;ROOT_DEV = s->s_dev;printk(KERN_INFO"VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",s->s_type->name,s->s_flags & MS_RDONLY ? " readonly" : "",MAJOR(ROOT_DEV), MINOR(ROOT_DEV));return 0;}

在上述代码中,调用了sys_mount()sys_chdir()系统调用函数。

又到linux的系统调用了,sys_mount系统调用可参考该篇文章:

/kai_ding/article/details/9050429

由于小生知识与精力有限,如若文章存在不妥的地方,欢迎批评,也可与小生一起讨论(iriczhao@)。哈哈!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。