原创 initrd and initramfs

2009-12-14 13:42 4665 8 8 分类: MCU/ 嵌入式
书上说initrd和initramfs的关系很乱,特别是linux2.6内核对这两种映像的处理,让人看了心里慌张。
今天特意作个实验试验一下两种映像到底有什么不同,重点掌握busybox的init处理流程。

/**************************************************************************************************************/
先回顾一下:
内核的最后引导过程位于.../init/main.c
...
if(execute_command){
run_init_process(execute_command);
printk(KERN_WARNIG "Failed to execute %s. Attempting " "defaults...\n",execute_command);
}

run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");

panic("No init found.Try passing init= option to kernel.");

在大多数linux系统中,内核在启动过程会执行/sbin/init,这也是用户空间第一个要执行的程序。
busybox提供的init,与标准的system init有很大的不同,它的处理流程如下:
1 为init设置信号处理过程;
2 初始化控制台;
3 读取inittab文件,/etc/inittab;
4 执行系统初始化命令行,缺省的是使用/etc/init.d/rcS文件;
5 执行wait类型的inittab命令;
6 执行once类型的inittab命令;
一旦完成以上工作,init进程会循环执行所有respawn和askfirst类型的inittab命令。
/**************************************************************************************************************/
/*                   cpio格式的initramfs                              */
/**************************************************************************************************************/
一:
用busybox编译一个最小rootfs(添加必要的目录,设备文件)
panasonic@linux-tricy:~/rootfs> tree .
.
|-- bin
|   |-- busybox
|   `--sh -> busybox
|-- dev
|   |-- console
|   |-- null
|   `-- ttyAMA0
|-- etc
|   |-- init.d
|-- linuxrc -> ../bin/busybox
|-- proc
|-- root
|-- sbin
|   |-- init -> ../bin/busybox
|-- sys
|-- tmp
制作cpio格式的initramfs
panasonic@linux-tricy:~/rootfs> find . |cpio -o -H newc |gzip > ../image.cpio.gz

二:
编译内核,内核配置打开initial RAM filesystem and RAM disk (initramfs/initrd)support 以及RAM block device support。
点击看大图

点击看大图

三:
qemu测试脚本如下
#!/bin/sh
qemu-system-arm -nographic -kernel ./zImage -initrd ./image.cpio.gz -append "console=ttyAMA0"
点击看大图


结果可怕的内核恐慌出现了...
原因是linux2.6内核中,initramfs挂载了rootfs后将执行/init,并将所有的工作都交给这个根目录下的init完成。

四:
将根目录下的linuxrc符号链接改名为init
panasonic@linux-tricy:~/rootfs> ls -ls |grep init
0 lrwxrwxrwx 1 panasonic users   11 12-05 14:13 init -> bin/busybox
重新打包cpio格式的映像,运行qemu测试

点击看大图

结果busybox抱怨说找不到/etc/init.d/rcS脚本,并且不停的输出打不开tty2/3/4的信息。
原来在busybox缺省的init模式中,当系统没有/etc/inittab文件时,它有一套缺省的模式,按下面配置执行:
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
如果busybox检测到/dev/console不是串口控制台,init还要执行下面的动作:
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
上面的结果很好的验证了这些动作的流程。

五:
改进
既然initramfs的入口在根目录下面的init脚本,我们可以直接绕过busybox自己写一个简单的init;然后为解决找不到tty2/3/4设备的问题,可以手工在dev目录下建立这些设备节点,这里用mdev功能自动生成,使用这个功能前需要挂载系统的伪文件系统。

点击看大图

#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
/bin/sh
为init脚本设置可执行权限
chmod +x init
再次打包cpio格式的映像测试

点击看大图

结果init脚本运行到/bin/sh的时候提示can't not access tty;job control turned off
产生这个错误的原因是我们的SHELL是直接运行在内核的console上的,而console是不能提供控制终端(terminal)功能的,所以必须把SHELL运行在tty设备上,才能消除这个错误。

六:
再改进
(1)写/etc/init.d/rcS脚本
这个脚本实际是要执行系统的初始化操作。我们把前面的init脚本改造一下,将最后的/bin/sh命令删除,然后移到 etc/init.d目录下,改名为rcS。
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
hostname qemu
(2)initramfs不需要linuxrc,而且如果没有init文件,内核就不认为它是一个有效的initramfs,因而不安装它,导致内核panic。于是,在rootfs目录下,把busybox安装的linuxrc改名为init
(3)重新生成新的initramfs
(4)用QEMU试验一下

点击看大图

结果busybox还是打出了一行警告信息,按回车激活这个控制台!
“please press Enter to active this console”
用tty命令看看当前的终端设备文件名:
# tty
/dev/console
它还是console,不是tty设备,所以问题没有解决。
busybox的缺省init模式无法满足我们的要求,我们还是要写inittab,定制自己的init初始化流程。


七:
再改进
(1)要写自己的inittab,需要理解busybox的inittab文件格式。
busybox的inittab文件与通常的inittab不同,它没有runlevel的概念,语句功能上也有限制。inittab语句的标准格式是
<id>:<runlevels>:<action>:<process>
各字段的含义如下
<id>:
id字段与通常的inittab中的含义不同,它代表的是这个语句中process执行所在的tty设备,内容就是/dev目录中tty设备的文件名。由于是运行process的tty设备的文件名,所以也不能象通常的inittab那样要求每条语句id的值唯一。
<runlevels>:
busybox不支持runlevel,所以此字段完全被忽略。
<action>:
为下列这些值之一:
sysinit, respawn, askfirst, wait,once, restart, ctrlaltdel, shutdown
其含义与通常的inittab的定义相同。特别提一下askfirst,它的含义与respawn相同,只是在运行process前,会打出一句话 “please press Enter to active this console”,然后等用户在终端上敲入回车键后才运行process。
<process>:
指定要运行的process的命令行。

(2)写个mini的inittab
理解了busybox的inittab格式,就可以写mini的inittab:
::sysinit:/etc/init.d/rcS
ttyAMA0::respawn:/bin/sh
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r

把这个文件放到/etc目录下。为了执行reboot命令时避免提示找不到/etc/fstab文件,我们再在etc目录下创建一个空文件
touch fstab

(3)重新打包映像,qemu测试,这回系统启动后直接进入控制台提示符,输入tty命令可以看到,目前console工作在tty设备上,这里是integrator CP开发板上面的串行口。

点击看大图


/**************************************************************************************************************/
/*                   gzip格式的initrd                                                                                                                               */
/**************************************************************************************************************/
传统gzip格式的initrd在linux2.6内核中也支持,但是处理流程有很大的差异。
一:
制作gzip格式的initrd映像,先把格式化好的映像文件挂在回环设备上,然后把根文件系统拷贝过去,最后打包成gzip格式。
panasonic:~> dd if=/dev/zero of=./initrd.img bs=1k count=8192
8192+0 records in
8192+0 records out
8388608 bytes (8.4 MB) copied,0.0699054 秒,120 MB/秒
panasonic # mke2fs -F -m0 ./initrd.img
panasonic # mount -t ext2 -o loop ./initrd.img /mnt
linux-tricy:/home/panasonic # ls /mnt
lost+found
panasonic # cp -a ./rootfs/* /mnt
panasonic # sync
panasonic # ls /mnt
bin  dev  etc  init  init~  lost+found  proc  root  sbin  sys  tmp
panasonic # umount /mnt
panasonic # gzip -9 ./initrd.img
 
二:
根文件系统不变,测试qemu的脚本如下
#!/bin/sh
qemu-system-arm -nographic -kernel ./zImage -initrd ./initrd.img.gz -append "console=ttyAMA0"

点击看大图

结果内核恐慌又来了...
留意图上黑色的一行,其实VFS已经挂载过rootfs,就是ext2格式的映像!但是为什么最后还是找不到根文件系统呢?
initrd的处理流程如下(linux2.6)
1. boot loader把内核以及initrd文件加载到内存的特定位置。
2. 内核判断initrd的文件格式,如果不是cpio格式,将其作为image-initrd处理。
3. 内核将initrd的内容保存在rootfs下的/initrd.image文件中。
4. 内核将/initrd.image的内容读入/dev/ram0设备中,也就是读入了一个内存盘中。
5. 接着内核以可读写的方式把/dev/ram0设备挂载为原始的根文件系统。
6. .如果/dev/ram0被指定为真正的根文件系统,那么内核跳至最后一步正常启动。
7. 执行initrd上的/linuxrc文件,linuxrc通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。
8. /linuxrc执行完毕,常规根文件系统被挂载
9. 如果常规根文件系统存在/initrd目录,那么/dev/ram0将从/移动到/initrd。否则如果/initrd目录不存在, /dev/ram0将被卸载。
10. 在常规根文件系统上进行正常启动过程 ,执行/sbin/init。
这就是说initrd只是系统启动过程的中间步骤,目的是加载需要的模块驱动,为挂载真正的根文件系统做准备!
当然你也可以在传递给内核启动的命令行中指定root=/dev/ram0,这样initrd就是最终的根文件系统了。
initrd的核心就是根目录下面的linuxrc脚本。

三:
再验证
将/etc/inittab 改名inittab-bak
/etc/init.d/rcS末尾加上/bin/sh,然后移动到根目录rootfs代替linuxrc脚本
重新按照一的步骤制作initrd映像,qemu测试

点击看大图

结果顺利进入系统,这时候我们输入命令exit看一下,内核结束这个中间步骤,挂载真正的根文件系统时候panic了,HOHO~

点击看大图

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
8
关闭 站长推荐上一条 /3 下一条