深度探索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/bashlibdl.so.2 => /vita/sysroot/lib/libdl.so.2libgcc_s.so.1 =>/vita/cross-tool/i686-none-linux-gnu/lib/libgcc_s.so.1libc.so.6 => /vita/sysroot/lib/libc.so.6vita@baisheng:/vita/initramfs$ ldd lib/libdl.so.2libc.so.6 => /vita/sysroot/lib/libc.so.6ld-linux.so.2 => /vita/sysroot/lib/ld-linux.so.2vita@baisheng:/vita/initramfs$ ldd lib/libc.so.6ld-linux.so.2 => /vita/sysroot/lib/ld-linux.so.2vita@baisheng:/vita/initramfs$ ldd lib/ld-linux.so.2vita@baisheng:/vita/initramfs$ ldd lib/libgcc_s.so.1libc.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.mzph.cn/news/209109.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

tomcat篇---第二篇

系列文章目录 文章目录 系列文章目录前言一、tomcat容器是如何创建servlet类实例?用到了什么原理?二、tomcat 如何优化?三、熟悉tomcat的哪些配置?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女…

Web应用JSON数据保护(密码算法、密钥、数字签名和数据加密)

1.JSON(JavaScript Object Notation) JSON是一种轻量级的数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据。JSON通过简单的key-value键值对来描述数据,可以被广泛用于网络通信、数据存储等各种应用场景&#xff0…

Python面向对象基础

Python面向对象基础 一、概念1.1面向对象的设计思想1.2 面向过程和面向对象1.2.1 面向过程1.2.2 面向对象1.2.3 面向过程和面向对象的优缺点 二、类和对象2.1 概念2.2 类的定义2.3 对象的创建2.3.1 类中未定义构造函数2.3.2 类中定义构造函数 2.4 类的设计 三、类中的成员3.1 变…

Python教程-数组

作为软件开发者,我们总是努力编写干净、简洁、高效的代码。在本文中,我们将探索 Python 数组的各种特性和功能。我们将学习如何在 Python 中创建、操作和使用数组,以及数组与 Python 编程语言中的其他数据结构有何不同。我们的目标是提供有关…

资源文件、布局管理器、样式表拓展

QT 资源文件 提供了和本地路径无关的资源管理。 图片资源的获取&#xff1a;阿里巴巴矢量图库&#xff08;&#x1f448; 安全链接&#xff0c;放心跳转&#xff09; widget.ui .qrc widget.h #ifndef WIDGET_H #define WIDGET_H#include <QtWidgets>namespace Ui { c…

Plonky2 = Plonk + FRI

Plonky2由Polygon Zero团队开发&#xff0c;实现了一种快速的递归SNARK&#xff0c;据其团队公开的基准测试&#xff0c;2020年&#xff0c;以太坊第一笔递归证明需要60s生成&#xff0c;而于今Plonky2在 MacBook Pro上生成只需 170 毫秒。 下面将逐步剖析Plonky2。 整体构造 …

活久见—当设置不同坐标系统时,ArcMap中的图形相关位置关系会变化

这两天一件十分神奇的事情发生了&#xff1a;当设置不同坐标系统时&#xff0c;ArcMap中的图形相对位置关系会变化。 事情起因是这样的&#xff1a;博主和同行用ArcMap同时验证2个相邻多边形的相对位置关系&#xff0c;见下图图1和图2的多边形&#xff0c;在博主的ArcMap中&am…

大电流H桥电机驱动电路的设计与解析(包括自举电路的讲解,以IR2104+LR7843为例)

大电流H桥电机驱动电路的设计与解析&#xff08;包括自举电路的讲解&#xff0c;以IR2104LR7843为例&#xff09; 电机驱动板主要采用两种驱动芯片&#xff0c;一种是全桥驱动&#xff08;如&#xff1a;HIP4082&#xff09;&#xff0c;一种是半桥驱动&#xff08;如&#xff…

单片机语言--C51语言的数据类型以及存储类型以及一些基本运算

C51语言 本文主要涉及C51语言的一些基本知识&#xff0c;比如C51语言的数据类型以及存储类型以及一些基本运算。 文章目录 C51语言一、 C51与标准C的比较二、 C51语言中的数据类型与存储类型2.1、C51的扩展数据类型2.2、数据存储类型 三、 C51的基本运算3.1 算术运算符3.2 逻辑…

docker学习(七、搭建mysql8.2主从)

一、主库搭建 1.构建主库镜像 # 运行mysql镜像&#xff0c;配置端口3307为主库 docker run -p 3307:3306 --name mysql-master --privilegedtrue -v /mydata/mysql-master/log:/var/log/mysql -v /mydata/mysql-master/data:/var/lib/mysql -v /mydata/mysql-master/conf:/etc…

前端:HTML+CSS+JavaScript实现轮播图2

前端&#xff1a;HTMLCSSJavaScript实现轮播图2 1. 和之前版本的区别2. 实现原理3. 针对上述的改进3. 参考代码 1. 和之前版本的区别 之前发布的那篇关于轮播图的文章在这&#xff1a;前端&#xff1a;HTMLCSSJavaScript实现轮播图&#xff0c;只能说存在问题吧&#xff01;比…

HuggingFace学习笔记--Prompt-Tuning、P-Tuning和Prefix-Tuning高效微调

1--Prompt-Tuning 1-1--Prompt-Tuning介绍 Prompt-Tuning 高效微调只会训练新增的Prompt的表示层&#xff0c;模型的其余参数全部固定&#xff1b; 新增的 Prompt 内容可以分为 Hard Prompt 和 Soft Prompt 两类&#xff1b; Soft prompt 通常指的是一种较为宽泛或模糊的提示&…

搭乘“低代码”快车,引领食品行业数字化转型全新升级

数字化技术作为重塑传统行业重要的力量&#xff0c;正以不可逆转的趋势改变着企业经营与客户消费的方式。 在近些年的企业数字化服务与交流过程中&#xff0c;织信团队切实感受到大多数企业经营者们从怀疑到犹豫再到焦虑最终转为坚定的态度转变。 在这场数字化转型的竞赛中&a…

VS2009和VS2022的错误列表可复制粘贴为表格

在VS2019或VS2022中&#xff0c;可看到如下错误列表&#xff1a; 如果复制这两行错误信息&#xff1a; 然后把它粘贴到word文件&#xff0c;就可以看到以下表格&#xff1a; 严重性 代码 说明 项目 文件 行 禁止显示状态 错误(活动) E0020 未定义标识符 "dd"…

0基础学java-day16-(坦克大战[1])

一、 坦克大战游戏演示 1 游戏演示 2.为什么写这个项目 3.写项目前的提醒 二、java 绘图坐标体系 1 坐标体系-介绍 2 坐标体系-像素 3 介绍-快速入门 package com.hspedu.draw;import javax.swing.*; import java.awt.*;/*** author 林然* version 1.0* 演示如何在面板画圆…

【python可视化系统源码】基于爬虫与可视化的电影推荐系统课题背景、目的、意义、研究思路、研究方法

该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程等学习内容。 目录 一、项目介绍&#xff1a; 二、文档学习资料&#xff1a; 三、模块截图&#xff1a; 四、开发技术与运行环境&#xff1a; 五、代码展示&#xff1a; 六、数据库表截图&#xff1a…

从简单到入门,一文掌握jvm底层知识文集。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

matlab RGB三元组和十六进制的转换

matlab画柱状图改颜色的时候&#xff0c;用三元组的形式&#xff0c;范围是[0&#xff0c;1] 我们获得了十六进制 到网站转换为[0,255] https://c.runoob.com/front-end/55/ 然后将得到的值/255 输入matlab就可以了

Bean作用域和生命周期

小王学习录 前言Bean的作用域什么是Bean的作用域Bean的六种作用域1. 单例作用域 singleton2. 原型作用域 prototype (多例作用域)3. 请求作用域 request4. 会话作用域 session5. 全局作用域 application6. Http WebSocket作用域 websocket Spring的执行流程和Bean的生命周期Spr…

网页设计--第6次课后作业

试用Vue相关指令完成对以下json数据的显示。显示效果如下&#xff1a; 其中&#xff1a;gender1 显示为女&#xff0c;gender2显示为男。价格超过30元&#xff0c;显示“有点小贵”。价格少于等于30元&#xff0c;则显示“价格亲民”。 data: {books: [{"id": "…