Linux系统启动详解(二)
上节讲到了Linux启动大体流程,及grub的作用,本节主要扯扯initramfs的那些事,并且通过简单修改initramfs,将整体操作系统运行到了内存中。
3 initramfs
3.1 简述
在2.4内核中initrd为boot loader initialized RAM Disk,linux启动前boot loader先将initrd文件解压,并加载到内存中。boot loader加载内核后,会先运行initrd,再switch_root到真正root fs。
在2.6内核中将initrd升级为initramfs,与initrd相比其作用相同,但是initramfs流程更加简洁,即解压initramfs后,运行init脚本,所以任务交由init脚本来完成。
更多initrd与initramfs的说明可参见文献[3]。
[3]:Linux2.6内核的Initrd机制解析http://www.ibm.com/developerworks/cn/linux/l-k26initrd/
3.2 init基本流程
initramfs的真正作用加载硬件模块和文件系统模块,用于切换到root device上的真正的root fs上。当然由于root fs应该支持存在于md或者dm设备上,所以在initramfs期间也会加载md和dm模块,如果md编译进行了内核的话,从加载的模块中是查看不到的。
以下是通过在initramfs中的init文件中添加log,分析一个大概的开机流程,非细节。
1) 解压并加载initramfs到内存后,当前root fs就是initramfs,所以/下所有文件都来源于initramfs。
2) 通过挂载/proc和/sys;mknod创建基本的设备/dev/console、/dev/null等。
3) udev规则对硬件进行扫描,加载对应模块,创建对应设备,对于grub.conf中设置的root设备,若创建成功,则会在/dev/下创建/dev/root的链接指向对应真实设备。
4) 与此同时,无限循环判断/dev/root是否已存在,若不存在,再次执行所有udev规则,尝试是否有新硬件已准备就绪,判断40次后(约20秒),仍未找到,则报出“no root device fonud, sleep forever”
5) 通过root device挂载真正的root fs。
6) 判断/proc目录是否存在,作为root fs已挂载成功的标准(此时的/proc仅仅是个空目录)。
7) 判断真正的root fs的init脚本是否存在,一般是/sbin/init
8) root fs已挂载到/sysroot目录后,执行switch_root /sysroot /sbin/init。
9) switch_root命令执行后,initramfs的任务全部完成,切换到真正的root fs,开始执行/sbin/init
3.3 initramfs过程中的模块加载情况
begin init:
Opening /proc/modules: No such file or directory
pre udev:
Module Size Used by
after udev:
Module Size Used by
sd_mod 37157 0
crc_t10dif 1507 1 sd_mod
sr_mod 16162 0
cdrom 39769 1 sr_mod
mptspi 16537 0
mptscsih 35302 1 mptspi
mptbase 91106 2 mptspi,mptscsih
scsi_transport_spi 26117 1 mptspi
pata_acpi 3667 0
ata_generic 3611 0
ata_piix 22588 0
dm_mod 76856 0
after mount root fs, before switch root:
Module Size Used by
ext4 353883 1
mbcache 7918 1 ext4
jbd2 88969 1 ext4
sd_mod 37157 2
crc_t10dif 1507 1 sd_mod
sr_mod 16162 0
cdrom 39769 1 sr_mod
mptspi 16537 1
mptscsih 35302 1 mptspi
mptbase 91106 2 mptspi,mptscsih
scsi_transport_spi 26117 1 mptspi
pata_acpi 3667 0
ata_generic 3611 0
ata_piix 22588 0
dm_mod 76856 0
3.4 initramfs的解压&&压缩
1、 解压:
-bash-4.1# file initramfs-2.6.32-71.el6.5.vsds.x86_64.img
initramfs-2.6.32-71.el6.5.vsds.x86_64.img: gzip compressed data, from Unix, last modified: Tue Dec 18 17:25:14 2012, max compression
# gzip文件格式,所以需要先mv为gz文件
-bash-4.1# mv initramfs-2.6.32-71.el6.5.vsds.x86_64.img initramfs-2.6.32-71.el6.5.vsds.x86_64.img.gz
-bash-4.1# file initramfs-2.6.32-71.el6.5.vsds.x86_64.img
initramfs-2.6.32-71.el6.5.vsds.x86_64.img: ASCII cpio archive (SVR4 with no CRC)
-bash-4.1# gunzip initramfs-2.6.32-71.el6.5.vsds.x86_64.img.gz
initramfs-2.6.32-71.el6.5.vsds.x86_64.img
#此时为cpio格式
-bash-4.1# mkdir initramfs
-bash-4.1# cd initramfs
-bash-4.1# cpio -id ../initramfs
initramfs/ initramfs-2.6.32-71.el6.5.vsds.x86_64.img
-bash-4.1# cpio -id < ../initramfs-2.6.32-71.el6.5.vsds.x86_64.img
72953 blocks
2、 压缩:
-bash-4.1# find . | cpio -c -o > ../initramfs-2.6.32-71.el6.5.vsds.x86_64.img
72953 blocks
-bash-4.1# gzip ../initramfs-2.6.32-71.el6.5.vsds.x86_64.img
-bash-4.1# cp ../initramfs-2.6.32-71.el6.5.vsds.x86_64.img.gz /boot
3.5 实例---将整个操作系统运行在内存
以下使用方案并未真正应用,以下纯属个人理解,供参考。
3.5.1 优点 && 缺点
1、优点:
1) 运行速度更快。开机后,由于系统运行在内存,运行速度要更快。
2) 文件系统不会损坏。系统异常断电,或者其它破坏操作,不会引起操作系统损坏。
3) 节省存储介质空间。操作系统可以进行压缩后存放在可存储介质中(压缩率约80%),甚至可以存放在网络环境上,开机后,解压再到内存。
4) 便于多台系统统一升级升级。若系统压缩文件存放在网络环境上,那么文件升级,使用这个文件的任意台设备升级过程就仅仅是重启就可以做到。
2、缺点:
1) 开机速度略慢。由于启动过程需要将操作系统解压到内存,会略慢一点,约10 ~ 20秒不明显。
2) root fs无法存储数据。由于系统运行在内存,重启后新存储的丢失。因此,一般需要存储数据的分区单独挂载一个非易失的介质上。
3) 内存要求更大。由于内存固定部分会划分一个ram disk,用于运行操作系统。对于操作系统就需要900M空间的应用,内存2G就不足以支撑整个系统。
4) 更新系统较麻烦。对于需要频率更改系统的环境,若要生效,需要重新制作压缩文件,较麻烦。
3.5.2 应用场景
目前嵌入式系统大多使用此方案,应该是基于其较小的存储空间,及无用户写入数据要求而决定的。(本人不是做嵌入式的,纯属个人猜测哈)
3.5.3 实现思路
思路一:就是把整体系统做成initramfs。(未测试)
initramfs就是一个小文件系统,也是开机第一个加载的文件系统,所以把整体系统都做成initramfs,那么系统启动只需要2个文件即可,一是内核vmlinux;二是initramfs。
也就是说在initramfs最后要切换到真正的root fs时,不切换到了,直接运行系统中的/sbin/init。
但是initramfs可能会达到300M(root fs900M左右),boot loader会不会需要加载很长时间?
思路二:在initramfs运行过程中,将压缩的系统解压并复制到内存中运行
在上节已说明initramfs其实就是执行一个init脚本,简单修改脚本流程。当寻到到root device设备后,将挂载root device挂载到/sysroot修改为,挂载root device到其它挂载点,再从root device中找到之前制作的压缩的系统文件,将文件解压到ramdisk中,挂载ramdisk到/sysroot,再切换到真正的root fs。
3.5.4 实现代码
以下仅根据思路二为基本,完成的一个简单模型。
将系统文件解压并复制到内存,那么先得将真正的root fs制作为一个系统文件,再通过initramfs的过程将其加载到内存。
1、 root fs文件ramlinux.img.gz的制作。
开始只将参考资料[2]中文件制作为了ramlinux.img.gz(压缩后约80M,解压后给不到200M),开机系统就panic,想必是应该缺少文件导致,所以,就将整个/下所有文件都制作到ramdisk.img.gz中,系统正常运行。
制作ramdisk.img.gz的脚本为mkrootfs.sh如下:
#!/bin/sh
rootfs="/mnt/ram"
umount $rootfs
rm -rf $rootfs
# make ram disk and mount
dd if=/dev/zero of=/dev/ram bs=1M count=900
echo y | mkfs.ext2 /dev/ram
mkdir $rootfs
mount -o loop /dev/ram $rootfs
# cp file to ram disk
pre_dir="/"
#pre_dir="/mnt/sdf1/"
# all file in dir cp to ram disk
cpdir=(bin boot cgroup etc home lib lib64 media opt sbin selinux srv usr var)
for dir_i in ${cpdir[@]}
do
echo "cp -av $pre_dir$dir_i $rootfs"
cp -av $pre_dir$dir_i $rootfs
done
# just mkdir, not cp file
mkdir=(proc sys tmp mnt root)
for dir_j in ${mkdir[@]}
do
echo "mkdir $rootfs/$dir_j"
mkdir $rootfs/$dir_j
done
# create dev device
mkdir $rootfs/dev
dev=`find /dev`
for dev_i in ${dev[@]}
do
if [ $dev_i = "/dev/ram" -o $dev_i = "/dev" ]
then
echo "find $dev_i pass........................................... "
continue
fi
echo "cp -R $dev_i $rootfs/dev"
cp -R $dev_i $rootfs/dev
done
# do ramlinux.img from ram disk
umount $rootfs
dd if=/dev/ram of=/mnt/bigspace/ramlinux.img bs=1M
gzip /mnt/bigspace/ramlinux.img
2、 initramfs加载root fs文件ramlinux.img.gz。
initramfs挂载真正的root fs,是通过执行initramfs的mount目录下的*.sh来完成的,在mount目录下加入98mount-ramdisk.sh,移除99mount-root.sh(原挂载root device到/sysroot的脚本)。由98mount-ramdisk.sh来完成系统文件的解压,及挂载。
98mount-ramdisk.sh如下:
#!/bin/sh
echo "Mount root device for get img file"
mount -o defaults --rw /dev/root /mnt
echo "zcat img file to ram"
/bin/zcat /mnt/root/ramlinux.img.gz > /dev/ram
umount /mnt
echo "mount -o loop /dev/ram /sysroot"
mount -o loop /dev/ram /sysroot
echo "ls /sysroot"
/bin/ls /sysroot/
[ -d "/sysroot/proc" ] || echo "proc not exist 5555555555555555555........"
3、 完成以上步骤后,将initramfs解压,拷贝98mount-ramdisk.sh到mount目录下,删除99mount-root.sh,重新生成initramfs,覆盖/boot下的原文件,重启即生效。
4、 其它注意。由于解压ramlinux.img需要zcat命令,但此命令initramfs中未加入,因此,还需要从系统中拷贝zcat命令到initramfs的bin目录下。(拷贝命令前,需要通过ldd /bin/zcat查看该命令是否有对应动态链接库,如果有要一并cp-R到initramfs的对应目录下,否则仅拷贝命令,命令是无法执行的)
5、 重启后的系统环境:
a) 重启后,系统从挂载信息即可看出当前系统已运行在内存中,并且touch的新文件,重启无未保存,挂载信息如下:
-bash-4.1# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 886M 886M 0 100% /
tmpfs 2.0G 52M 1.9G 3% /dev/shm
/dev/sda2 146M 32M 106M 24% /var/log
(/的挂载设备应该不是/dev/sda1,应该是df命令在当前模型下显示的问题,应该是/dev/ram,由于只是简单模型搭建,很多配置未修改成导致。)
b) /dev/sda1是grub.conf中设置的root device,也是ramlinux.img文件真正存储的介质。
-bash-4.1# cat /boot/grub/grub.conf
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE: You do not have a /boot partition. This means that
# all kernel and initrd paths are relative to /, eg.
# root (hd0,0)
# kernel /boot/vmlinuz-version ro root=/dev/sda1
# initrd /boot/initrd-[generic-]version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/boot/grub/splash.xpm.gz
hiddenmenu
title SkySAN Storage System (2.6.32-71.el6.5.vsds.x86_64)
root (hd0,0)
kernel /boot/vmlinuz-2.6.32-71.el6.5.vsds.x86_64 ro root=UUID=212c95a4-56d0-44fc-93ce-73b5f3e8cf2a rd_NO_LUKS rd_NO_LVM rd_NO_MD rd_NO_DM LANG=en_US.UTF-8 SYSFONT=latarcyrheb-sun16 KEYBOARDTYPE=pc KEYTABLE=us
initrd /boot/initramfs-2.6.32-71.el6.5.vsds.x86_64.img
3.6 init脚本中加入log信息
在理解init脚本过程中,为了更深入的理解init的作用,及当前环境状态,往往需要打印一些我们自己需要的信息,在打印到屏幕的同时,如果可能保存文件,便于多次阅读更好。
方法如下:
logfile="/tmp/logfile"
# tee用于将输出到屏幕的信息,同时保存文件,-a:不覆盖文件原内容,相当于>>
echo "begin init -------------------------------------------------------" 2>&1 | tee -a $logfile
lsmod 2>&1 | tee -a $logfile
sleep 5
……
#在swith_root前,将文件拷贝到/dev/目录下,切换到root fs后,可以保留。
cp $logfile /dev/
exec switch_root "$NEWROOT" "$INIT" $initargs
……
Linux系统启动详解(二)的更多相关文章
- Linux学习之CentOS(二十一)--Linux系统启动详解
在这篇随笔里面将对Linux系统的启动进行一个详细的解释!我的实验机器是CentOS6.4,当然对于现有的Linux发行版本,其系统的启动基本上都是一样的! 首先我们来看下Linux系统启动的几个 ...
- Linux基本配置和管理 4 ---- Linux系统启动详解
1 系统启动的流程 BIOS -> MBR+boot code -> 执行引导程序: GRUB -> 加载内核 -> 执行init -> 运行runlevel 2 启动详 ...
- Linux系统启动详解(一)
本篇主要以Centos为例,讲述整个Linux系统启动过程,包括了grub引导,initramfs流程,/sbin/init执行rc.sysinit及rc的大体流程. 另外,本篇有一个实例来说明,将整 ...
- Linux系统启动详解
系统启动流程 通过下图认识下Linux系统的总体启动流程. BIOS BIOS一般负责检查硬件和查找启动设备. MBR:Boot Code MBR只是一段引导代码,真正的引导是由引导程序去执行的. G ...
- Linux入门-8 Linux系统启动详解
系统启动流程 BIOS MBR GRUB KERNEL INIT 单用户修改root密码 GRUB加密 系统启动流程 BIOS MBR: Boot Code 执行引导程序 - GRUB 加载内核 执行 ...
- Linux 信号详解二(信号分类,信号处理,kill)
信号分类 信号分为可靠信号和不可靠信号 不可靠信号的缺点 ①:处理完信号,需要重新再注册信号:②信号可能丢失. Linux已经对缺点①做了优化,现在的不可靠问题主要指的是信号可能丢失 信号还可以分为实 ...
- Linux系统启动详解(三)
上节已系统initramfs已启动完成,将系统控制权交给了真正的rootfs的/sbin/init,下面就是/sbin/init干活的时间了. 4 /sbin/init initramfs ...
- Linux dts 设备树详解(二) 动手编写设备树dts
Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 前言 硬件结构 设备树dts文件 前言 在简单了解概念之后,我们可以开始尝试写一个 ...
- Linux init详解(转)
Linux init详解 一.什么是INIT: init是Linux系统操作中不可缺少的程序之一. 所谓的init进程,它是一个由内核启动的用户级进程. 内核自行启动(已经被载入内存,开始运行,并已初 ...
随机推荐
- linux 同步 rsync的使用——远程服务器同步配置
一.背景介绍 由于需要和其他系统进行对接.文件的逻辑地址通过接口传递,而文件的实体需要通过服务器间的同步进行传输.在同事的建议下选择了rsync. 二.RSYNC介绍 RSYNC 有多种方式进行同步, ...
- Unity Lighting - Light Probes 光照探针(十)
Light Probes 光照探针 Only static objects are considered by Unity’s Baked or Precomputed Realtime GI s ...
- 【snmp】Linux开启snmp及查询
1.Linux snmp 1.安装snmp yum install -y net-snmp* 2.备份snmp配置 cp /etc/snmp/snmpd.conf /etc/snmp/snmpd.co ...
- 集合set、map、list
一.set 无序.可重复 public static void main(String[] args){ Set<String> set=new HashSet<String> ...
- (转)一篇写的简明易懂的logging模块
转:http://kenby.iteye.com/blog/1162698 一.从一个使用场景开始 开发一个日志系统, 既要把日志输出到控制台, 还要写入日志文件 import logging # 创 ...
- rest_framework基础
简介 为什么要使用REST framework? Django REST framework 是一个强大且灵活的工具包,用以构建Web APIs. - 在线可视的API,对于赢得你的开发者们十分有用 ...
- ES6的新特性(20)—— Module 的加载实现
Module 的加载实现 上一章介绍了模块的语法,本章介绍如何在浏览器和 Node 之中加载 ES6 模块,以及实际开发中经常遇到的一些问题(比如循环加载). 浏览器加载 传统方法 HTML 网页中, ...
- Python Pygame (4) 图像的变换
Pygame中的transform模块可以使得你能够对图像(也就是Surface对象)做各种动作,列如左右上下翻转,按角度转动,放大缩小......,并返回Surface对象.这里列举了transfo ...
- Scrum7
冲刺阶段的总结 一.各个成员今日完成的任务 组员 任务分工 贡献 林泽宇 团队分工.撰写博客.修改完善需求规格说明书.整理代码规范 李涵 后端架构设计 尹海川 logo设计修改.数据库数据 郏敏杰 课 ...
- 02慕课网《进击Node.js基础(一)》——CommonJs标准
是一套规范管理模块 每个js 为一个模块,多个模块作为一个包 node.js和Couchdb是对其的实现: 不同于jQuery 模块:定义.标识.引用(地址/模块名称) 模块类型: 核心模块http ...