Linux根文件系统的初始化

根文件系统的安装分为两个阶段:

1、安装rootfs文件系统
2、安装实际的文件系统
其中,安装rootfs的流程在<Linux文件子系统(VFS)的初始化>已经描述,它是一个虚拟的文件系统,并且进程0的根目录和工作目录设置为这个“根文件系统”。
第二阶段实际的文件系统是在内核初始化即将结束的时候进行,根据内核的编译和启动选项进行配置。

相关概念:

ram disk,ramfs,initramfs
ram disk是一个固定大小的内存区域,有特定的文件系统格式。
从内核2.5.x开始,先前的“initial ramdisk”协议升级为"initial ramfs" (initramfs)协议,initramfs内容使用initrd协议使用的相同内存缓冲区协议传递,但内容不同。 initramfs缓冲区包含一个存档,该存档被扩展为ramfs文件系统;
initramfs缓冲区格式基于“newc”或“crc” CPIO格式,可以使用cpio(1)工具创建。 可以使用gzip(1)进行压缩。 因此,一个有效的initramfs buffer版本是可以是一个.cpio.gz文件。


ramfs、rootfs、tmpfs之间的关系:

 

相关的编译选项

  • CONFIG_DEVTMPFS: 维护devtmpfs文件系统以挂载到/dev; 这会在启动时创建tmpfs/ramfs文件系统实例。如果未启用CONFIG_TMPFS,则使用ramfs。影响函数如:devtmpfs_mount。
  • CONFIG_BLK_DEV_INITRD: (影响函数:initrd_load)初始RAM文件系统和RAM disk(initramfs/initrd)支持; initial RAM文件系统是ramfs,它由引导加载程序(loadlin或lilo)加载,并在正常引导过程之前以root身份挂载。 它通常用于加载安装“真正的”根文件系统所需的模块,如果还包括RAM disk支持(BLK_DEV_RAM),这也使能initial RAM disk(initrd)支持,并在内核大小上增加15 KB(在某些架构上更多)。
  • CONFIG_BLK_DEV_RAM: (影响函数如:rd_load_image())RAM块设备支持,使能将允许使用一部分RAM内存作为块设备,以便您可以在其上创建文件系统,读写等;它通常用于在linux初始化期间将最小根文件系统的副本从软盘加载存储到RAM。大部分用户不需要。
  • CONFIG_BLK_DEV_MD: (md_run_setup())用于使能RAID支持。

相关的启动参数:

  • "rdinint=": 对应: ramdisk_execute_command
  • "init=": 对应: execute_command
  • "root=": 对应: saved_root_name
  • "rootfstype": 对应: root_device_name
  • "noinitrd": 对应:  mount_initrd
     

三种挂载根文件系统的方式:

a)编译到内核中的所有必需的设备和文件系统驱动程序,没有initrd。 init/main.c:init()将调用prepare_namespace()来挂载最终的根文件系统,基于“root=”选项和可选的“init=”运行一些其他init二进制文件,而不是在init/main.c末尾列出的:init()。

 

b)一些设备和文件系统驱动程序构建为模块,并存储在initrd中。 initrd必须包含一个二进制'/ linuxrc',来加载这些驱动程序模块。 也可以通过linuxrc挂载最终的根文件系统并使用pivot_root系统调用。 initrd通过prepare_namespace()安装和执行。

 

c)使用initramfs。 必须跳过对prepare_namespace()的调用。 这意味着二进制文件必须完成所有工作。 所述二进制文件可以通过修改usr/gen_init_cpio.c或通过新的initrd格式(cpio存档)存储到initramfs中。 它必须被称为“/init”。 这个二进制文件负责执行prepare_namespace()所做的所有事情。

 

initramfs同initrd的差异:

a)initrd是个单独的文件,而initramfs archive链接在内核镜像中。

b)initrd是个gizip压缩的文件系统镜像(如ext2,这就需要内核中有相应的驱动),而initramfs是一个压缩的cpio集合,解压的代码不仅小而且相关代码可以放在__init中,执行完丢弃。

c)initrd执行程序(/initrd不是/init)完成设置后会返回内核,而init不会。

d)当更改根文件系统,initrd要用pivot_root随后卸载ramdisk,但initramfs是rootfs:不需要pivot_root也不需要卸载。而是删除rootfs中的内容释放空间,使用新的根来覆盖它,执行新的init程序。

 

相关函数:

在进程1中加载真正的根文件系统:

下面以启动参数

init=/linuxrc slub_debug root=/dev/mmcblk0p1 rw rootwait loglevel=8 earlyprintk console=ttyAMA0

来浏览下相关代码:

static int __ref kernel_init(void *unused)
{
	int ret;

	kernel_init_freeable(); //加载根文件系统
	/* need to finish all async __init code before freeing the memory */
	async_synchronize_full();
	free_initmem();
	mark_rodata_ro();
	system_state = SYSTEM_RUNNING;
	numa_default_policy();

	flush_delayed_fput();

	if (ramdisk_execute_command) { //由启动参数rdinit配置,不进入该分支
		ret = run_init_process(ramdisk_execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d)\n",
		       ramdisk_execute_command, ret);
	}

	/*
	 * We try each of these until one succeeds.
	 *
	 * The Bourne shell can be used instead of init if we are
	 * trying to recover a really broken machine.
	 */
	// 由启动参数“init=”配置, 需要在真实的根文件系统中存在“/linuxrc”程序
	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.");
}

 kernel_init_freeable中调用prepare_namespace挂载更文件系统

static noinline void __init kernel_init_freeable(void)
{
	/*
	 * Wait until kthreadd is all set-up.
	 */
	wait_for_completion(&kthreadd_done);

   	... ...

	do_basic_setup();

	/* Open the /dev/console on the rootfs, this should never fail */
	if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
		pr_err("Warning: unable to open an initial console.\n");

	(void) sys_dup(0);
	(void) sys_dup(0);
	/*
	 * check if there is an early userspace init.  If yes, let it do all
	 * the work
	 */

	if (!ramdisk_execute_command)
		ramdisk_execute_command = "/init";

	//如果ramdisk_execute_command不可以访问,调用prepare_namespace
	if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
		ramdisk_execute_command = NULL;
		prepare_namespace();
	}

	/*
	 * Ok, we have completed the initial bootup, and
	 * we're essentially up and running. Get rid of the
	 * initmem segments and start the user-mode stuff..
	 *
	 * rootfs is available now, try loading the public keys
	 * and default modules
	 */

	integrity_load_keys();
	load_default_modules();
}

 在prepare_namespace中从启动参数“root=”获取设备的文件名称


/*
 * Prepare the namespace - decide what/where to mount, load ramdisks, etc.
 */
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 the known devices to complete their probing
	 *
	 * Note: this is a potential source of long boot delays.
	 * For example, it is not atypical to wait 5 seconds here
	 * for the touchpad of a laptop to initialize.
	 */
	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()) // 宏CONFIG_BLK_DEV_INITRD未使能,返回0
		goto out;

	/* wait for any asynchronous scanning to complete */
	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;
    // 在do_mount_root将文件系统安装在/root目录,调用ksys_chdir("/root")改变进程当前目录
	mount_root(); 
out:
	devtmpfs_mount("dev");
    //移动rootfs文件系统根目录上的已安装文件系统的安装点。
	sys_mount(".", "/", NULL, MS_MOVE, NULL);
	sys_chroot(".");
}

rootfs并未被卸载,只是被隐藏了。

挂载过程中的打印:

[    6.004666] EXT2-fs (mmcblk0p1): warning: mounting unchecked fs, running e2fsck is recommended
[    6.272461] VFS: Mounted root (ext2 filesystem) on device 179:1.
[    6.582633] devtmpfs: mounted
[    6.590264] Freeing unused kernel memory: 296K (80640000 - 8068a000)
[    6.922110] random: nonblocking pool is initialized
[    8.820749] smsc911x 4e000000.ethernet eth0: SMSC911x/921x identified at 0xa0a80000, IRQ: 31

 

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核的文件系统初始化是系统启动过程中的一个重要步骤,它负责加载文件系统并挂载为系统的目录。 在启动过程中,BIOS或UEFI首先加载引导加载程序(bootloader),如GRUB或Syslinux等。引导加载程序会加载内核映像(vmlinuz)到内存,并将控制权转交给内核。 内核启动后,它会执行一系列初始化操作,其中包括文件系统初始化文件系统通常存储在硬盘或闪存设备上,可以是ext4、XFS、Btrfs文件系统格式。 文件系统初始化的过程主要包括以下几个步骤: 1. 设备初始化:内核会初始化硬件设备,如磁盘控制器、网络接口等,以便后续能够访问文件系统所在的设备。 2. 文件系统驱动加载:内核会加载相应的文件系统驱动模块,以支持对特定文件系统格式的读写操作。 3. 设备挂载:内核会据引导参数或配置文件指定的设备信息(如硬盘分区、NFS共享等),找到文件系统所在的设备,并将其挂载为系统的目录(/)。 4. 初始化进程:一旦文件系统成功挂载,内核会运行用户空间的第一个进程,通常是init或systemd。这个进程负责启动其他用户空间进程和服务。 总而言之,文件系统初始化Linux系统启动过程中的一个关键步骤,它负责加载和挂载文件系统,并启动用户空间的初始化进程,从而完成系统的初始化工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值