深度探索Linux操作系统 —— 构建initramfs

在这里插入图片描述

系列文章目录


深度探索Linux操作系统 —— 编译过程分析
深度探索Linux操作系统 —— 构建工具链
深度探索Linux操作系统 —— 构建内核
深度探索Linux操作系统 —— 构建initramfs


文章目录

  • 系列文章目录
  • 前言
  • 一、为什么需要 initramfs
  • 二、initramfs原理探讨
  • 三、构建基本的initramfs
  • 四、将硬盘驱动编译为模块
    • 1、配置devtmpfs
    • 2、将硬盘控制器驱动配置为模块
    • 3、自动加载硬盘控制器驱动
      • (1)内核向用户空间发送事件
      • (2)udev加载驱动和建立设备节点


前言

    一般而言,桌面、服务器等通用系统都使用 initramfs。部分嵌入式系统中,也会使用 initramfs,甚至有的使用 initramfs 作为最终的根文件系统。那么什么是 initramfs 呢?很难用一句话将 initramfs 的作用描述清楚,或许可以将 initramfs 定位为内核通往根文件系统的桥梁。


一、为什么需要 initramfs

    鸡和蛋的问题:内核要加载这些模块或者运行这些程序才能正确识别根文件系统所在的设备,但是保存这些模块或者程序的根文件系统又存储在这些设备上。

    内核开发者们设计了 initramfs 机制。initramfs 是一个临时的文件系统,其中包含了必要的设备如硬盘、网卡、文件系统等的驱动以及加载驱动的工具及其运行环境,比如基本的 C 库,动态库的链接加载器等等。同时,那些处理根文件系统在 RAID 、网络设备上的程序也存放在 initramfs 中。由第三方程序(如 Bootloader )负责将 initramfs 从硬盘装载进内存。以驱动硬盘为例,内核就不必再从硬盘,而是从已经加载到内存的 initramfs 中获取硬盘控制器等相关驱动了,继而可以驱动硬盘,访问硬盘上的根文件系统,从而解决了前面提到的鸡和蛋的矛盾。

    在初始化的最后,内核运行 initramfs 中的 init 程序,该程序将探测硬件设备、加载驱动,挂载真正的文件系统,执行文件系统上的 /sbin/init,进而切换到真正的用户空间。真正的文件系统挂载后,initramfs 即完成了使命,其占用的内存也会被释放。

二、initramfs原理探讨

    在 2.4 以及更早版本的内核中,内核使用的是 initrdinitrd 是基于ramdisk技术的,而 ramdisk 就是一个基于内存的块设备,因此 initrd 也具有块设备的一切属性。比如 initrd 容量是固定的,一旦创建 initrd 时设定了一个大小,就不能再进行动态调整。

    ramfsramdisk 有着本质的区别,ramdisk 本质上是基于内存的一个块设备,而 ramfs 是基于缓存的一个文件系统。因此,ramfs 去除了前述块设备的一些限制。比如,ramfs 根据其中包含的文件大小可自由伸缩;增加文件时,自动分配内存;删除文件时,自动释放内存。更重要的是,ramfs 是基于已有的缓存机制,因此不必再像 ramdisk 那样需要和缓存之间进行多余的复制一环。

    从 2.6 开始,内核开发人员基于 ramfs 开发了 initramfs 替代 initrd

    当 2.6 版本的内核引导时,在挂载真正的根文件系统之前,首先将挂载一个名为 rootfs 的文件系统,并将 rootfs 的根作为虚拟文件系统目录树的总根。那么为什么要使用 rootfs 这么一个中间过程呢?原因之一还是为了解决鸡和蛋的问题。内核需要根文件系统上的驱动以及程序来驱动和挂载根文件系统,但是这些驱动和程序有可能没有编译进内核,而在根文件系统上。如果不借助第三方,内核是没有办法挂载真正的根文件系统的。而 rootfs 虽然名称为 rootfs ,但是并不是什么新的文件系统,事实上,其就是一个 ramfs,只不过换了一个名称。换句话说,rootfs 是在内存中的,内核不需要特殊的驱动就可以挂载 rootfs,所以内核使用 rootfs 作为一个过渡的桥梁。

    在挂载了 rootfs 后,内核将 Bootloader 加载到内存中的 initramfs 中打包的文件解压到 rootfs 中,而这些文件中包含了驱动以及挂载真正的根文件系统的工具,内核通过加载这些驱动、使用这些工具,实现了挂载真正的根文件系统。此后,rootfs 也完成了历史使命,被真正的根文件系统覆盖(overmount)。但是 rootfs 作为虚拟文件系统目录树的总根,并不能被卸载。但是这没有关系,前面我们已经谈到了,rootfs 基于 ramfs,删除其中的文件即可释放其占用的空间。

三、构建基本的initramfs

# 1
mkdir initramfs
cd initramfs

# 2
# /vita/initramfs/init
#!/bin/bash
echo "Hello Linux!"
exec /bin/bash

# 3
mkdir bin
cp ../sysroot/bin/bash bin/

bash 依赖

vita@baisheng:/vita/initramfs$ ldd bin/bash
	libdl.so.2 => /vita/sysroot/lib/libdl.so.2
	libgcc_s.so.1 =>/vita/cross-tool/i686-none-linux-gnu/lib/libgcc_s.so.1
	libc.so.6 => /vita/sysroot/lib/libc.so.6

vita@baisheng:/vita/initramfs$ ldd lib/libdl.so.2
	libc.so.6 => /vita/sysroot/lib/libc.so.6
	ld-linux.so.2 => /vita/sysroot/lib/ld-linux.so.2
	
vita@baisheng:/vita/initramfs$ ldd lib/libc.so.6
	ld-linux.so.2 => /vita/sysroot/lib/ld-linux.so.2
	
vita@baisheng:/vita/initramfs$ ldd lib/ld-linux.so.2

vita@baisheng:/vita/initramfs$ ldd lib/libgcc_s.so.1
	libc.so.6 => /vita/sysroot/lib/libc.so.6

    bash 依赖于libc、libdl 以及 libgcc_s.so.1,因此,我们需要在 initramfs 中安装这三个库,以及安装加载动态库的动态加载/链接器。
    根据依赖关系可见,libdl 依赖 libc 和动态链接器,libgcc 只依赖 libclibc 仅依赖动态链接器,而动态链接器不依赖其他任何库,因此,我们不再需要安装其他库到 initramfs 中。

四、将硬盘驱动编译为模块

1、配置devtmpfs

    Linux2.6.18 开始采用 udev/dev 目录使用了基于内存的文件系统 tmpfs 管理设备文件。

    2009 年初,开发人员又提出了 devtmpfs ,并在同年年底被 Linux 2.6.32 正式收录。内核引导时,devtmpfs 将所有注册的设备在 devtmpfs 中建立相应的设备文件,一旦进入用户空间,在启动 udev 前,就可以将 devtmpfs 挂载到 /dev 目录下。

    也就是说,在启动 udev 前,devtmpfs 中已经建立了初步的设备文件,一般启动程序不必再等待 udev 建立设备节点,甚至在某些嵌入式系统上,不再需要 udev 创建设备节点,因为这个基本的 /dev 已经足够,从而缩短了系统的启动时间。同 rootfs 类似,devtmpfs 也不是新设计的文件系统,如果内核配置支持 tmpfs ,那么其就是 tmpfs;否则,devtmpfs 就是 ramfs,只不过换了一个名字而已。

2、将硬盘控制器驱动配置为模块

    接下来重新编译内核和模块。内核和模块可以使用单独的命令分开编译,也可以使用一条 make 命令同时编译内核和模块。编译完成后,将模块暂时安装在 “/vita/sysroot/lib/modules” 目录下。

vita@baisheng:/vita/build/linux-3.7.4$ make bzImage
vita@baisheng:/vita/build/linux-3.7.4$ make modules
vita@baisheng:/vita/build/linux-3.7.4$ make \
		INSTALL_MOD_PATH=$SYSROOT modules_install

最终安装的硬盘控制器驱动模块包括:

vita@baisheng:/vita$ ls sysroot/lib/modules/3.7.4/kernel/drivers/ata/

ahci.ko ata_piix.ko libahci.ko

我们将其复制到 initramfs 中。

3、自动加载硬盘控制器驱动

    从 2.6 版内核开始,Linux 采用 udev 管理驱动模块的加载以及设备节点的管理。每当内核发现新的设备,便通过 NETLINK 向用户空间发送新设备事件,该事件中记录了设备的相关信息。用户空间的 udev 服务进程收到内核事件后,根据事件中携带的信息,首先判断该设备的驱动是否已经加载,如果没有,则加载驱动。驱动加载后,内核会再次向用户空间报告发现新设备事件,这时设备已经成功驱动了,并且主次设备号等信息也已经准备好了,udev 收到事件后,或者为设备建立节点,或者执行某些特定的操作。整个过程如图4-19所示。

在这里插入图片描述

(1)内核向用户空间发送事件

    PC 机上的硬盘控制器,无论是 IDE 接口的,还是 SATA 接口的,一般都是通过 PCI 总线连接到计算机上的。内核在引导时,PCI 子系统将进行初始化,枚举总线上的设备,并尝试为设备匹配驱动;然后将收集到的设备相关信息组织为 uevent 事件;接着调用 kobject_uevent ,通过 NETLINK 将组织好的 uevent 发送到用户空间,通知 udev 有新设备了。简单地讲,内核的工作就是探测并收集设备信息,将其包装到 uevent 事件中,然后发送到用户空间。

    事实上,无论是发现新的设备,还是有新的驱动载入,抑或是用户向 sysfs 中的 uevent 写入字符串,内核都将调用函数 kobject_uevent 向用户空间发送事件。

    结构体 kobj_uevent_env 用来保存收集到的设备相关信息,所以在函数 kobject_uevent_env 中,首先为 kobj_uevent_env 申请了一块内存,即变量 env 指向的内存,用来临时存放准备发送到用户空间的设备相关信息。

    然后向该内存中添加了三个默认的变量,包括 ACTIONDEVPATHSUBSYSTEM 。 其中 ACTION 指的是热插拔的动作,如 “add”,“remove”,“change” 等。DEVPATH 指的是设备在 sysfs 文件系统中注册的设备路径,比如笔者的硬盘 sdaDEVPATH 是 “/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda” 。SUBSYSTEM 一般是指设备所在的总线,比如笔者的硬盘是挂在 PCI 总线上的,因此该变量的值是 “pci”。

    pci_uevent 又向 uevent 中追加了 pci class 、 vendor id 、 device id 以及 MODALIAS 等变量,其中 MODALIAS 需要重点关注,其是由设备所在总线、vendor ID、device ID 等相关参数连接而成的一个字符串。在接下来的章节中,读者将看到,用户空间的 udev 恰恰就是根据这个变量为设备匹配驱动模块的。

    除了总线外,如果硬盘控制器所属的 class 或者 type 也需要继续向 uevent 中追加变量,则继续调用硬盘控制器所属的 class 或者 type 中的相应的函数,这里不再继续分析了。最终,内核向用户空间发送的 uevent 事件包含的大致的内容如下,其中不同变量之间使用 “\0” 进行分隔。

在这里插入图片描述

(2)udev加载驱动和建立设备节点

   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/230884.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在Windows 11中,至少有四种方法可以创建用户

本文介绍如何在Windows11上添加另一个用户(或多个用户)。 使用设置添加其他用户 这是创建新用户的“正常”方式。这是一个简单的过程,允许你添加Microsoft、本地或家庭帐户。 添加Microsoft或本地帐户 按照以下步骤将Microsoft帐户添加到Windows 11或创建新的本地用户。…

论文阅读:PointCLIP: Point Cloud Understanding by CLIP

CVPR2022 链接:https://arxiv.org/pdf/2112.02413.pdf 0、Abstract 最近,通过对比视觉语言预训练(CLIP)的零镜头学习和少镜头学习在2D视觉识别方面表现出了鼓舞人心的表现,即学习在开放词汇设置下将图像与相应的文本匹配。然而,…

实现手机扫码——扫描识别路由器参数

有个应用是批量自动检测无线路由器,检测前需要自动登录路由器的管理界面进行设置,如设置wifi参数、连接模式,或者恢复出厂设置等。进入管理界面的登录用户名是admin,密码则各不相同。此外也需要知道路由器的MAC地址,因…

Qt基础-程序打包发布方法

本文讲解Qt程序打包发布方法。 一、使用Qt自带的windeployqt 生成可运行的包 准备将Qt生成的exe拷入到单独的文件夹,并进行命名,本文命名为packDemorun,并将文件放到D盘(自己随意放置) 1、找到Qt自带的命令终端 2、启动命令终端 3、输入:cd /d D:\packDemorun,进入文…

质量工程化,交付快速化

质量和速度之间权衡让人很难取舍,而通过推进质量工程,以系统化的方式识别和优化系统痛点,可以帮助团队构建既快又好的精益软件生产系统。原文: Quality Engineered, Speed Delivered 所有人都想要更快的速度。 但需要解决复杂问题: 权衡质量会…

小程序时代的机遇:开发成功的知识付费平台

知识付费平台不仅为知识创作者提供了广阔的变现渠道,同时也为用户提供了更为个性化、精准的学习体验。本篇文章,小编将为大家讲解知识付费小程序开发相关的知识。 一、小程序时代的背景 知识付费作为小程序领域中的“大热门”,有着非常高的…

整数在内存中的存储

整数和浮点数在内存中的存储方式是不一样的,今天,我们来具体学习一下 文章目录 整数在内存中的存储浮点数在内存中的存储 整数在内存中的存储 我们在之前就已经了解过了整数有原码,反码,补码的形式,这三种方式都是二进…

springboot 集成Dubbo2.7.8 ,连接zookeeper 提示错误 zookeeper not connected

Dubbo 连接zookeeper时&#xff0c;提示“zookeeper not connected” java.lang.IllegalStateException: zookeeper not connectedat org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperClient.<init>(CuratorZookeeperClient.java:83) ~[dubbo-2.7.8.jar:2.…

【每周一测】Java阶段四第二周学习

目录 1、在MyBatis中&#xff0c;当实体类中的属性名和表中的字段名不一样&#xff0c;除了&#xff08; &#xff09;都可以实现属性和数据的映射。 2、下列数组定义及赋值&#xff0c;错误的是&#xff08; &#xff09; 3、关于会话跟踪以下说法错误的&#xff08; &…

C++ Qt开发:Qt的安装与配置

Qt是一种C编程框架&#xff0c;用于构建图形用户界面&#xff08;GUI&#xff09;应用程序和嵌入式系统。Qt由Qt公司&#xff08;前身为Nokia&#xff09;开发&#xff0c;提供了一套跨平台的工具和类库&#xff0c;使开发者能够轻松地创建高效、美观、可扩展的应用程序。其被广…

多线程(进阶一:锁策略)

一、乐观锁和悲观锁 二、轻量级锁和重量级锁 三、自旋锁和挂起等待锁 四、普通互斥锁和读写锁 五、公平锁和非公平锁 六、可重入锁和不可重入锁 七、synchronized和Linux的mutex锁的简单比较 八、synchronized的自适应 一、乐观锁和悲观锁 乐观锁&#xff1a;在加锁之前…

启动游戏出现concrt140.dll错误的8种解决方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是找不到concrt140.dll文件。这个错误通常会导致程序无法正常运行&#xff0c;给用户带来困扰。本文将介绍找不到concrt140.dll无法继续执行代码的8个方法&#xff0c;同时探讨concrt140.dll丢…

【活动】内容运营活动,你做对了吗?

内容运营活动&#xff0c;首先应该确认本次活动的主题&#xff0c;明确目标受众&#xff0c;分析这类用户的使用场景&#xff0c;感兴趣的话题等&#xff0c;结合市场的热点&#xff0c;探讨活动的形式&#xff0c;过程节点&#xff0c;活动奖励等内容&#xff08;头脑风暴形式…

Unity打包到Webgl平台以及遇到的问题

Unity打包到Webgl平台以及遇到的问题 参考网站 Unity打包WebGL的全过程及在打包和使用过程中会遇到的问题(本地测试)-CSDN博客 unity打包到Webgl 并配置能正常运行 这里我用的是Unity2022.3.3f1c1版本 有两种方法 1、配置本地web服务 2、安装vsCode>添加插件LiveServe…

使用git出现的问题

保证 首先保证自己的git已经下载 其次保证自己的gitee账号已经安装并且已经生成ssh公钥 保证自己要push的代码在要上传的文件夹内并且配置文件等都在父文件夹&#xff08;也就是文件没有套着文件&#xff09; 问题 1 $ git push origin master gitgitee.com: Permission de…

laravel的ORM 对象关系映射

Laravel 中的 ORM&#xff08;Eloquent ORM&#xff09;是 Laravel 框架内置的一种对象关系映射系统&#xff0c;用于在 PHP 应用中与数据库进行交互。Eloquent 提供了一种优雅而直观的语法&#xff0c;使得开发者可以使用面向对象的方式进行数据库查询和操作。 定义模型&…

Git 请输入一个提交信息以解释此合并的必要性

操作方法&#xff1a;按住Ctrl加下面的某个字母

linux-man命令的使用及练习

目录 1. 命令概述 2. 使用 3. 练习 ?man services时报错&#xff1a;No manual entry for services的解决办法 4. man命令中常用按键以及用途 1. 命令概述 Linux提供了丰富的帮助手册&#xff0c;当你需要查看某个命令的参数时不必到处上网查找&#xff0c;只要man一下即…

PID控制参数整定(调节方法)原理+图示+MATLAB调试

PID控制参数整定&#xff08;调节方法&#xff09;原理图示MATLAB调试 Chapter1 PID控制参数整定&#xff08;调节方法&#xff09;原理图示MATLAB调试序一、P参数选取二、I的调节三、D的调节四、总结 Chapter2 PID参数调整&#xff0c;个人经验&#xff08;配输出曲线图&#…

多人聊天程序

服务端 import java.io.*; import java.net.*; import java.util.ArrayList; public class Server{public static ServerSocket server_socket;public static ArrayList<Socket> socketListnew ArrayList<Socket>(); public static void main(String []args){try{…
最新文章