【linux kernel】linux内核如何挂载根文件系统
2022/1/26 7:04:50
本文主要是介绍【linux kernel】linux内核如何挂载根文件系统,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
linux内核最后如何挂载根文件系统
一、前世今生
在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_name
是mtd
或者ubi
类型的根设备,则运行mount_block_root()
挂载文件系统。
(2)【方式二】:调用initrd_load()
进行早期根文件系统的挂载,如果是mount_initrd
为true的情况下,将执行根文件系统挂载操作。在linux内核中包含两种挂载早期根文件系统
的机制,初始化RAM磁盘(initrd)是一种老式的机制。而initramfs
是新的用于挂载早期根文件系统的机制。对于initrd
和initramfs
机制的目的:用于执行早期的用户空间程序;在挂载正真(最后的)根文件系统之前加载一些必须的设备驱动程序。
(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_NFS if (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_FD if (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; } } else change_floppy("root floppy"); } #endif #ifdef CONFIG_BLOCK create_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
系统调用就参考该篇文章啦:
https://blog.csdn.net/kai_ding/article/details/9050429
小生由于知识与精力有限,如若文章存在有不妥的地方,欢迎批评,也可与小生一起讨论(iriczhao@163.com)。哈哈!
这篇关于【linux kernel】linux内核如何挂载根文件系统的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法
- 2024-08-21【Linux】gnome桌面环境切换KDE Plasma
- 2024-08-19如何安装 VMware Tools (macOS, Linux, Windows)
- 2024-08-15Linux部署Scrapy教程:入门级指南