为什么需要initramfs

在Linux内核被加载到内存并运行后,内核进程最终需要切换到用户太的进程来使用计算机,而用户进程又存在于外存储设备上,比如systemd进程,通常systemd进程所在的存储设备也是Linux真正的根文件系统所在的位置,我们知道内核源码是没有包含驱动程序的,驱动程序在外存储设备上,那么问题来了,要切换到systemd进程,就需要外存储的驱动,但是没有驱动又没办法访问外存储,这就进入先有鸡还是先有蛋的问题了(那到底是先有鸡还是先有蛋呢:)),这个时候initramfs就闪亮登场了。

为什么不将所有驱动都写入内核而要用initramfs

第一,除非是一个专用系统,目标系统的硬件平台是固定不变的,否则,对于一个通用操作系统,比如Linux的桌面发行版,将运行在各种不同的硬件平台上。因此,根文件系统可能存储在各种各样的介质上,比如IDE硬盘、SATA硬盘、SCSI硬盘、Flash存储器,以及随着技术的发展,不断出现的新的存储设备。为了能够兼容更多的硬件平台,显然系统需要支持尽可能多的存储设备。但是如果将所有这些设备的驱动全部编译进内核,显然不是一个好办法。因为对于某个特定的硬件平台,可能只需要一个驱动即可,内核中的其他驱动根本用不上,将它们编译进内核只会徒增内核的尺寸、占用内存空间,尤其对于一些内存或者存储介质空间有限的设备,这个问题尤为明显。于是将这些驱动编译为模块,存储在根文件系统中,按需载入内存是一个解决问题的办法。

第二,根文件系统可能不在一个简单的硬盘上,比如当使用磁盘阵列RAID时,根文件系统可能横跨几个存储设备,或者根文件系统在某个网络设备上。以使用NFS挂载根文件系统为例,除了要支持网卡驱动外,还要进行网络配置,甚至还要进行网络认证。某些根文件系统经过压缩、加密,在挂载前需要解压缩、解密等操作。如果这些都由内核处理,将会使内核变得异常复杂,继而可能导致内核的稳定性、可靠性、灵活性等一系列的问题。因此,将复杂的操作移到用户空间是解决上述问题的一个思路。

为了解决上述问题,内核开发者们设计了initramfs机制。initramfs是一个临时的文件系统,其中包含了必要的设备如硬盘、网卡、文件系统等的驱动以及加载驱动的工具及其运行环境,比如基本的C库,动态库的链接加载器等等。同时,那些处理根文件系统在RAID、网络设备上的程序也存放在initramfs中。由第三方程序(如Bootloader)负责将initramfs从硬盘装载进内存。bootloader如何将内核与initramfs载入内存,敬请期待,写好后我将连接放到此处

以驱动硬盘为例,内核就不必再从硬盘,而是从已经加载到内存的initramfs中获取硬盘控制器等相关驱动了,继而可以驱动硬盘,访问硬盘上的根文件系统,从而解决了前面提到的鸡和蛋的矛盾。

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

initramfs原理

在讲解initramfs之前,先介绍initramfs的一个前辈initrd

在2.4以及更早版本的内核中,内核使用的是initrd。initrd是基于ramdisk技术的,而ramdisk就是一个基于内存的块设备,因此initrd也具有块设备的一切属性。比如initrd容量是固定的,一旦创建initrd时设定了一个大小,就不能再进行动态调整。而且,如同块设备一样,initrd 需要按照一定的文件系统格式进行组织,因此制作initrd时需要使用如mke2fs这样的工具“格式化”initrd,访问initrd时需要通过文件系统驱动。更重要的是,虽然initrd是一个伪块设备,但是从内核的角度看,其与真实的块设备并无区别,因此,内核访问initrd也需使用缓存机制,显然这是多此一举的,因为本身initrd就在内存中。

由于initrd太拉胯,于是initram呼之欲出

鉴于ramdisk机制的种种限制,Linus Torvalds提出了一个想法:能否将cache当作一个文件系统直接挂载使用?基于这个想法,Linus Torvalds基于已有的缓存机制实现了ramfs。ramfs与ramdisk有着本质的区别,ramdisk本质上是基于内存的一个块设备,而ramfs是基于缓存的一个文件系统。因此,ramfs去除了前述块设备的一些限制。比如,ramfs根据其中包含的文件大小可自由伸缩;增加文件时,自动分配内存;删除文件时,自动释放内存。更重要的是,ramfs是基于已有的缓存机制,因此不必再像ramdisk那样需要和缓存之间进行多余的复制一环。

伴随着ramfs的出现,从2.6开始,内核开发人员基于ramfs开发了initramfs替代initrd。那么initramfs是怎样工作的呢?

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

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

1、挂载rootfs

用于不同操作系统的文件系统其物理存储结构是不同的,但是Linux的虚拟文件系统通过为这些文件系统建立中间适配层,实现了Linux对各个文件系统的支持,Linux的虚拟文件系统将文件系统组织为树形结构。在初始化阶段,内核挂载rootfs文件系统,虚拟文件系统从无到有,rootfs的根作为虚拟文件系统这棵大树中的第一个节点,自然成为所有后来创建的节点的祖先。也就是说,虚拟文件系统目录树的根就是rootfs的根,本质上,rootfs就是一个ramfs文件系统。

通过挂载rootfs,虚拟文件系统的根目录已经建立起来,根目录已经可以容纳文件了。所以,接下来内核解压initramfs的内容到虚拟文件系统的根中,利用initramfs中的内容挂载并切换到真正的根文件系统。

2、解压initramfs到rootfs

在挂载了rootfs后,内核将Bootloader加载到内存中的initramfs中的文件解压到rootfs中。而这些文件中包含了驱动以及挂载真正的根文件系统的工具,内核通过加载这些驱动、使用这些工具实现挂载真正的根文件系统。

3、挂载并切换到真正的根目录

将initramfs成功解压后,挂载真正的根文件系统所需的驱动、程序等已经全部俱备,可以挂载真正的根文件系统了,在grub.cfg文件中对应的就是root=XXX。内核将真正的根文件系统挂载到initramfs文件系统中的/root目录下。

挂载真正的根文件系统后,rootfs中的内容已经没有保留的意义,但是并不能将rootfs卸载,因为rootfs是整个虚拟文件系统的根。因此,为了不占用内存空间,将rootfs中的内容(文件)释放掉即可,然后将真正的根文件系统移动到虚拟文件系统的根(即rootfs的根)下,最后再将进程的文件系统的namespace切换到真正的根文件系统。

initramfs文件基本操作

查看initramfs

lsinitrd

解压initramfs

/usr/lib/dracut/skipcpio initramfs-3.10.0-229.el7.x86_64.img | zcat | cpio -ivd

手动创建initramfs

[root@ct7_node02 initramfs]# find . | cpio -o -H newc | gzip -9 > /tmp/test.img

注意:修改完initramfs后,需要删除grub.cfg中的linux16 /vmlinuz-3.10.0-514.el7.x86_64 root=/dev/mapper/cl-root ro biosdevname=0 net.ifnames=0 console=ttyS0,115200n8不相关的内容,否则会导致内核启动失败。

创建一个最基本的initramfs

内核启动后会执行initramfs中的init程序,创建一个init,里面写入bash脚本,脚本添加两行信息,屏幕打印hello信息,并运行bash程序,exec是内核运行程序的函数,bash一般存放在/bin/bash路径,这里保持这个路径,其次是添加bash所依赖的库文件。

[root@ct7_node02 initramfs]# ll
total 4
drwxr-xr-x 2 root root 18 Aug  2 15:14 bin
-rwxr-xr-x 1 root root 49 Aug  2 15:13 init
drwxr-xr-x 2 root root 90 Aug  2 15:17 lib64
[root@ct7_node02 initramfs]# cat init
#!/bin/bashecho "Hello Michael"
exec /bin/bash
[root@ct7_node02 initramfs]# tree ./
./
|-- bin
|   `-- bash
|-- init
`-- lib64|-- ld-linux-x86-64.so.2|-- libc.so.6|-- libdl.so.2`-- libtinfo.so.52 directories, 6 files
[root@ct7_node02 initramfs]#

通过ldd命令查看bash所依赖的库文件,并将这些文件复制到上面的lib64目录中

[root@ct7_node02 initramfs]# ldd /usr/bin/bashlinux-vdso.so.1 =>  (0x00007ffcbd4aa000)libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f4268c01000)libdl.so.2 => /lib64/libdl.so.2 (0x00007f42689fd000)libc.so.6 => /lib64/libc.so.6 (0x00007f426863b000)/lib64/ld-linux-x86-64.so.2 (0x00007f4268e31000)
[root@ct7_node02 initramfs]#
[root@ct7_node02 tmp]# lsinitrd test.img
Image: test.img: 1.4M
========================================================================
Version: Arguments:
dracut modules:
========================================================================
drwxr-xr-x   4 root     root            0 Aug  2 15:17 .
drwxr-xr-x   2 root     root            0 Aug  2 15:14 bin
-rwxr-xr-x   1 root     root       960392 Aug  2 15:14 bin/bash
-rwxr-xr-x   1 root     root           49 Aug  2 15:13 init
drwxr-xr-x   2 root     root            0 Aug  2 15:17 lib64
-rwxr-xr-x   1 root     root       155064 Aug  2 15:15 lib64/ld-linux-x86-64.so.2
-rwxr-xr-x   1 root     root      2116736 Aug  2 15:15 lib64/libc.so.6
-rwxr-xr-x   1 root     root        19344 Aug  2 15:16 lib64/libdl.so.2
-rwxr-xr-x   1 root     root       174520 Aug  2 15:16 lib64/libtinfo.so.5
========================================================================

将这些文件制作成initramfs并添加到/boot目录下进行测试

同理其他的命令,例如ls、cd、pwd等的相关命令都可通过以上方式添加到initramfs文件中

initramfs详解-----初识initramfs相关推荐

  1. initramfs详解----设备文件系统

    initramfs的重要作用之一就是允许内核将保存根文件系统的存储设备的驱动不再编译进内核.本文我们就将硬盘驱动编译为模块,然后去识别硬盘设备.说道驱动,那么驱动一定是外部硬件设备的驱动,所以在介绍驱 ...

  2. HTML详解——初识html(一)

    文章目录 前言 一.HTML的基本结构 二.标题标签和段落标签 1.标题标签 2.段落标签 三.超链接标签 四.水平线和换行标签 五.特殊符号 六.图片引用 前言 提示:HTML简介: HTML(Hy ...

  3. BACnet协议详解——初识BACnet架构

    文章目录 BACnet协议架构 BACnet简化的架构 简化的四层BACnet体系结构选取 BACnet 网络的拓扑结构 安全 最后声明 BACnet协议架构 国际标准化组织在制定计算机网络通讯协议标 ...

  4. HTML详解——初识html(二)

    文章目录 前言 一.HTML列表 1.HTML3种列表 2.列表练习题 二.HTML表格 1.表格语义化标签 2.表格基本结构 3.表格综合运用 4.表格跨行.跨列合并 5.表格练习题 三.HTML表 ...

  5. git cherry-pick 详解 —— Git 学习笔记 18

    git cherry-pick 详解 初识 git cherry-pick(拣选) 拣选会提取某次提交的补丁,之后尝试将其重新应用到当前分支上. 这种方式在你只想引入特性分支中的某个提交时很有用. 假 ...

  6. group_concat函数详解

    https://blog.csdn.net/ys410900345/article/details/44828571 问了好多人,都不知道group_concat这个函数. 这个函数好啊,能将相同的行 ...

  7. web标准 浏览器介绍 开发工具介绍 HTML介绍 HTML颜色介绍 规范 HTML结构详解 {前端之前端初识}...

    前端之前端初识 前端初识 本节目录 一 web标准 二 浏览器介绍 三 开发工具介绍 四 HTML介绍 五 HTML颜色介绍 六 规范 七 HTML结构详解 一 web标准 web准备介绍: 1.w3 ...

  8. Android Preference详解之初识Preference及Preference系(一)

    Android Preference详解之初识Preference及Preference系(一) 转载:https://blog.csdn.net/google_huchun/article/deta ...

  9. 初识网络基础《网络七层模型详解》

    坚持原创输出,点击蓝字关注我吧 图片来自网络 目录 一.初识网络基础 二.网络七层模型详解 1.物理层 2.数据链路层 3.网络层 4.传输层 5.会话层 6.表示层 7.应用层 三.总结 一.初识网 ...

最新文章

  1. 如何快速融入团队(三)
  2. stackover flow载入巨慢
  3. 关于Domain-Specific Languages
  4. “如何写好科技论文之我见”结束语-如何写好科技论文之我见(十)
  5. Linux服务部署常用命令--top、free、df、scp
  6. JDK自带监控工具 jps、jinfo、jstat、jmap、jconsole
  7. Shadow Brokers 公布 2.1 万美元的 0day 订阅服务
  8. Analysis of Cloud Computing Architectures阅读笔记--提出了Screen虚拟化方法
  9. Vue 学习笔记(4)Vue-cli4 项目搭建 + 目录结构 + 项目打包、部署
  10. RegExp 误用解析
  11. C语言程序设计之猜数字游戏(随机数的讲解)
  12. 信息收集之namp扫描
  13. 关于CWMP基础(二)----TR069通信流程
  14. 指数加权移动平均详解 附代码实现(全网之最)
  15. Pano2VR制作全景漫游
  16. 爬虫---实现英语翻译(有道翻译)
  17. 国际电离层参考模型程序说明
  18. 蓝凌OA系统任意文件读取
  19. Python爬虫实例之淘宝商品比价定向爬取!爬虫还是很有意思的!
  20. 确认OHS版本的方法

热门文章

  1. Android Studio与Mysql连接的中文乱码问题
  2. SQL查询cross join 的用法(笛卡尔积)
  3. grep命令_Linux grep命令
  4. Windows Server 2012 VDI 部署的前提条件
  5. paper fast review :Deep Residual Shrinkage....
  6. 自己制作Chrome绿色版本。
  7. CAN/CAN_FD/Flexray的通信速率
  8. 飞凌OK6410、TE6410、FL6410 等ARM11 开发板的区别——非常详细哦
  9. 一篇文章搞定嵌入式看门狗watch dog概述与示例代码
  10. 域名和URL是什么?域名和IP是一一对应的吗?