公司网站模板源码,黄石做网站建设的,百事可乐网站建设优缺点,ico交易网站怎么做系列文章目录 Linux内核学习 Linux 知识#xff08;1#xff09; Linux 知识#xff08;2#xff09; WSL Ubuntu QEMU 虚拟机 Linux 调试视频 PCIe 与 USB 的补充知识 vscode 使用说明 树莓派 4B 指南 设备驱动畅想 Linux内核子系统 Linux 文件系统挂载 文章目录 系列文章…
系列文章目录 Linux内核学习 Linux 知识1 Linux 知识2 WSL Ubuntu QEMU 虚拟机 Linux 调试视频 PCIe 与 USB 的补充知识 vscode 使用说明 树莓派 4B 指南 设备驱动畅想 Linux内核子系统 Linux 文件系统挂载 文章目录 系列文章目录1. initrd 阶段的用户空间初始化脚本执行1.1 解压缩并挂载 initrd 文件系统1.2 加载介质驱动的机制 2. 挂载真实文件系统后的用户空间初始化脚本执行2.1 挂载真实文件系统 3. 执行用户空间初始化脚本3.1 内核中执行用户空间初始化脚本3.2 用户空间初始化脚本 4. 加载默认模块4.1 Linux 6.12.5 替代方案 5. 总结 1. initrd 阶段的用户空间初始化脚本执行
在 initrd 阶段内核会解压缩并挂载 initrd 文件系统。
1.1 解压缩并挂载 initrd 文件系统
populate_rootfs 函数负责解压缩 initrd 并将其挂载为根文件系统。以下是相关代码
// init/initramfs.c
static int __init populate_rootfs(void)
{char *err unpack_to_rootfs(__initramfs_start, __initramfs_size);if (err)panic(%s, err); /* Failed to decompress INTERNAL initramfs */if (initrd_start) {
#ifdef CONFIG_BLK_DEV_RAMint fd;printk(KERN_INFO Trying to unpack rootfs image as initramfs...\n);err unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start);if (!err) {free_initrd();goto done;} else {clean_rootfs();unpack_to_rootfs(__initramfs_start, __initramfs_size);}printk(KERN_INFO rootfs image is not initramfs (%s); looks like an initrd\n, err);fd sys_open(/initrd.image, O_WRONLY | O_CREAT, 0700);if (fd 0) {sys_write(fd, (char *)initrd_start, initrd_end - initrd_start);sys_close(fd);free_initrd();}done:
#elseprintk(KERN_INFO Unpacking initramfs...\n);err unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start);if (err)printk(KERN_EMERG Initramfs unpacking failed: %s\n, err);free_initrd();
#endif/** Try loading default modules from initramfs. This gives* us a chance to load before device_initcalls.*/load_default_modules();}return 0;
}
rootfs_initcall(populate_rootfs);1.2 加载介质驱动的机制
在 initrd 阶段内核会通过以下机制加载相关的介质驱动 检测根文件系统所在的设备 内核会根据 saved_root_name 变量来确定根文件系统所在的设备。这个变量通常由引导加载程序如 GRUB传递给内核。 加载默认模块 在 initrd 阶段内核会尝试加载默认的内核模块这些模块通常包括识别和访问根文件系统所在介质所需的驱动程序。这些模块可以通过 initrd 文件系统中的 /linuxrc 或 /init 脚本加载也可以通过内核的 load_default_modules 函数加载。 等待设备探测完成 内核会等待根文件系统所在的设备完成探测以确保设备驱动程序已经加载并准备好使用。
2. 挂载真实文件系统后的用户空间初始化脚本执行
在挂载真实文件系统后内核会切换到真实文件系统并执行用户空间的初始化脚本。这个过程主要发生在 prepare_namespace 函数之后。
2.1 挂载真实文件系统
prepare_namespace 函数负责挂载真实文件系统。以下是相关代码
// fs/namespace.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();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(.);
}3. 执行用户空间初始化脚本
3.1 内核中执行用户空间初始化脚本
在 prepare_namespace 函数之后内核会执行用户空间的初始化脚本。这是通过 kernel_init 函数实现的。以下是相关代码
// init/main.c
static noinline void __init kernel_init_freeable(void)
{wait_for_completion(kthreadd_done);gfp_allowed_mask __GFP_BITS_MASK;set_mems_allowed(node_states[N_MEMORY]);set_cpus_allowed_ptr(current, cpu_all_mask);cad_pid task_pid(current);smp_prepare_cpus(setup_max_cpus);do_pre_smp_initcalls();lockup_detector_init();smp_init();sched_init_smp();do_basic_setup();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);if (!ramdisk_execute_command)ramdisk_execute_command /init;if (sys_access((const char __user *) ramdisk_execute_command, 0) ! 0) {ramdisk_execute_command NULL;prepare_namespace();}load_default_modules();if (ramdisk_execute_command) {run_init_process(ramdisk_execute_command);} else {run_init_process(/sbin/init);}
}在上述代码中kernel_init_freeable 函数会检查是否存在用户空间的初始化脚本如 /linuxrc 或 /init如果存在则通过 run_init_process 函数执行该脚本。
3.2 用户空间初始化脚本
用户空间的初始化脚本如 /linuxrc 或 /init会在切换到真实文件系统后执行。以下是相关代码
#!/bin/sh
# /linuxrc# 加载必要的驱动模块
modprobe usb-storage
modprobe scsi_mod
modprobe ext4# 挂载真实文件系统
mount -a# 切换到真实根文件系统
pivot_root /mnt /dev/null
exec switch_root /sbin/init在上述脚本中modprobe 命令会加载必要的驱动模块以便挂载真实文件系统。
4. 加载默认模块
在 initrd 阶段内核会通过 load_default_modules 函数加载默认的内核模块。这些模块通常包括识别和访问根文件系统所在介质所需的驱动程序。
void __init load_default_modules(void)
{struct module *mod;int err;for_each_module(mod) {if (mod-state MOD_STATE_LIVE) {err try_to_load_module(mod-name);if (err)printk(KERN_ERR Failed to load module %s\n, mod-name);}}
}pivot_root linux,initrd如何切入linux文件系统
在 Linux 内核中load_default_modules 函数的主要功能是在系统启动过程中加载一组默认的内核模块这些模块对于系统的正常运行至关重要。以下是该函数的详细功能说明
加载默认模块 load_default_modules 函数负责加载系统启动时所需的一组默认内核模块。这些模块通常包括设备驱动程序、文件系统支持模块以及其他系统服务模块。模块初始化 在内核启动过程中需要初始化各种组件和驱动程序。load_default_modules 函数通过加载必要的模块来确保系统具备所需的驱动和功能从而保证系统能够正常运行。依赖关系处理 内核模块之间可能存在依赖关系。load_default_modules 函数确保这些依赖关系得到正确处理模块按照正确的顺序加载。这对于系统的稳定性和功能性至关重要。配置文件支持 该函数还会加载由系统配置文件指定的模块。这些配置文件通常位于 /etc/modules-load.d/ 目录下每个文件包含一个或多个模块名称系统启动时会自动加载这些模块。内核引导参数支持 除了配置文件load_default_modules 函数还会处理通过内核引导参数指定的模块加载。例如使用 modules_load 或 rd.modules_load 参数可以在启动时加载特定的模块。自动模块加载 对于大多数硬件设备Linux 内核会自动检测并加载相应的驱动模块。load_default_modules 函数通过使用存储在 /lib/modules/VERSION/modules.alias 中的数据库来实现这一点该数据库包含了模块与硬件设备之间的映射关系。手动模块加载 如果需要手动加载某个模块可以使用 modprobe 命令。该命令会根据模块的依赖关系加载所需的模块并将其添加到内核中。模块验证 在加载模块时load_default_modules 函数还会执行一些验证操作确保模块的完整性和兼容性。这包括检查模块的签名和验证 ELF 文件的合法性。模块初始化函数调用 该函数还会调用模块的初始化函数这些函数通常使用 module_init 宏进行声明。这些初始化函数负责设置模块的运行环境使其准备好供系统使用。
load_default_modules 函数在 Linux 内核启动过程中扮演着重要角色通过加载默认的内核模块确保系统具备所需的驱动和功能。它不仅处理模块之间的依赖关系还支持通过配置文件和内核引导参数加载特定模块从而保证系统的正常运行和硬件设备的正常工作。
4.1 Linux 6.12.5 替代方案
在 Linux 6.12.5 内核中确实没有 load_default_modules 函数。其替代实现主要通过 systemd-modules-load 服务来完成该服务在系统启动时自动加载内核模块。
5. 总结 虚拟根文件系统中没有 /init 在 initrd 阶段如果虚拟根文件系统中没有 /init 文件则会执行 prepare_namespace 函数来挂载真实根文件系统。为了识别根文件系统所在的介质如 SD、SCSI 等内核会通过 load_default_modules 函数加载相关的介质驱动。这些驱动程序确保内核能够正确识别和访问根文件系统所在的设备。 虚拟根文件系统中有 /init 在 initrd 阶段如果虚拟根文件系统中有 /init 文件则不执行 prepare_namespace 函数来挂载真实根文件系统真实根文件系统挂载将在 /init 脚本中进行。代码示例如下
#!/bin/sh
# /linuxrc# 加载必要的驱动模块
modprobe usb-storage
modprobe scsi_mod
modprobe ext4# 挂载真实文件系统
mount -a# 切换到真实根文件系统
pivot_root /mnt /dev/null
exec switch_root /sbin/initinitrd 典型的系统启动顺序 boot loader加载内核并初始化ram disk 内核把initrd转化成正常的ram disk并释放initrd使用的内存 initrd作为root被挂载赋予读写权限。 /linuxrc被执行(这可以是任何可执行文件如脚本运行在uid 0,可以做任何初始化)。 linuxrc挂载真正的根文件系统 linuxrc使用pivot_root系统调用把根文件系统放在根目录。 正常的启动序列(/sbin/init)在根文件系统上执行。 initrd文件系统被移去。 ☆