三十五、测试一下 通过前十一节的内容,我们可以说已经完全掌握了initramfs构建的方法,那么现在就测试一下,拿一个实际的initramfs来分析,看我们是否能理解多少。 我们选择Debian 4.0 AMD64 版本的initramfs作为目标进行分析,它的initramfs文件是initrd.img-2.6.18-6-amd64。首先用cpio命令把initramfs文件解开,然后打开其中的init脚本文件具体分析。好了,大家利用前面各节的知识开始吧。
作为提示,在这里转载一篇文章,来自http://blog.chinaunix.net/u/12679/showart_429816.html:
initrd执行顺序
这是安装过程中的笔记,放这里希望对大家有用。错误之处请指正。
(系统为debian etch, 以安装后默认的initrd.img-2.6.18-5为例)
解开initrd文件: 例如有initrd文件/boot/initrd.img, 用file命令看到这是个gzip压缩的文件,可以用下面的命令解开查看:
代码: mkdir /boot/myinit cd /boot/myinit zcat ../initrd.img | cpio -id(注:用mkinitrd命令默认产生的是cramfs格式的。如果文件格式是压缩的ramfs文件系统,可以直接mount之后查看:mount -t cramfs /boot/initrd.img /mnt/)
如果是2.6内核,因为采用的是cpio压缩,方法如下: cp /boot/initrd-***.img initrd.img.gz gunzip initrd.img.gz mkdir initrd mv initrd.img initrd cd initrd cpio -mdiv < initrd.img
在当前目录下就有一些目录和文件init,其中文件init是启动时加载initrd之后执行的脚本。
目录结构:
/bin/: 文件有busybox, mknod, sh, uname, cat, mount, pivot_root等
--------/sbin/: 文件有modprobe, depmod, udevd, udevdtrigger
--------/lib/: 文件有: ----------------(1) 可执行文件需要的动态库 ----------------(2) modules/: 内核模块 ----------------(3) udev/: udev需要的可执行文件
--------/lib64/: 文件有:x86_64程序装载器
--------/etc/: 与modprobe, udev相关的配置文件
--------/conf/: 有如下的文件 ----------------(1) modules: 列出了需要加载的模块 ----------------(2) arch.conf: 设置变量DPKG_ARCH=amd64 ----------------(3) initramfs.conf: 定义了一些变量
--------/scripts/: ----------------有如下的文件: ----------------functions: 定义了一些方便使用的函数 ----------------local和nfs: mount根目录时执行的脚本,一般mount本地系统,执行local ---------------- ----------------有如下的目录,其中放置各阶段执行的脚本: ----------------init_top/ ----------------init_premount/ ----------------init_bottom/ ----------------local_top/ ----------------local_premount/ ----------------local_bottom/
--------/init: 启动时加载initrd之后执行的脚本
生成initrd文件:
find . |cpio -o --dereference -H newc | gzip -9 > ../initrd.img
init文件执行流程:
--------1) 创建目录/dev, /root, /sys, /proc, /tmp, /var/lock,其中/root是下面根文件系统要mount的位置
--------2) mount系统proc和sys
--------3) 执行脚本/etc/udev/udev.conf,仅定义变量
--------4) mount udev设备:mount -t tmpfs -o size= udev /dev
--------5) 创建/dev/console, /dev/null, /dev/.initramfs
--------6) 导入/conf目录下的initramfs.conf, /conf/conf.d/目录以及/scripts/functions定义的变量和函数, 并且根据传递的内核参数设置相应的变量,其中比较重要的变量有: ----------------a) rootdelay=<时间>, 加载根文件系统前等待的时间。如果根文件系统在U盘上,一定要在这里或在grub的kernel后加上这个参数,等待发现U盘后再mount根文件系统,否则会出现找不到根文件系统的错误. ----------------b) panic=<时间> 系统panic后,隔多长时间再reboot。当你试验新内核或新initrd,经常panic而不想按开机按钮时可以加上这个参数
--------7) depmod -a 在/lib/modules/<内核版本号>/目录下产生modules.dep文件
--------8) 执行init_top目录下的脚本文件。这里只有一个framebuffer,它处理内核参数video=,vga=,splash等参数, 另外创建节点/dev/tty[0-8]
--------9) 用modprobe按顺序加载/conf/modules列出的模块。注意,udev需要unix模块,否则会出现错误: udevd[606]: error initializing udev socket
--------10) 执行/scripts/init-premount/下的脚本文件。 -------------10.1) 文件thermal: 它根据DPKG_ARCH的不同值加载相应需要的模块,并且加载fan和thermal模块。 -------------10.2) 文件udev: 执行如下过程: ------------------------a) mkdir -p /dev/.udev/db ------------------------b) udevd --daemon 以daemon形式启动udevd,大概用于监视hotplug设备的插拔 ------------------------c) mkdir -p /dev/.udev/queue/ (不知道这两个mkdir的用途) ------------------------d) udevtrigger 对于coldplug的设备,(也就是开机前插入的设备),触发设备的uevents,以便内核处理 ------------------------e) udevsettle 等待,直到内核uevents事件处理完
--------11) 执行/scripts/local, 它完成mount根文件系统的过程: ----------------11.1) 首先执行local-top/目录下的脚本 ------------------------11.1.1) 文件udev_helper: ----------------------------------a) 针对根文件系统设备(如通常的/dev/sda1, /dev/sda2等)不存在的情况,加载了模块ide_generic。我可以针对找不到硬盘的情况,加载模块aic94xx, sd_mod. ----------------11.2) 如果根文件系统设备还不存在,则年等待一段时间(默认180s),过了这段时间还找不到根文件系统设备就panic. ----------------11.3) 取得根文件系统的类型:先看有没有传递参数rootfstype=,如果没有就执行/bin/fstype,如果还不知道文件系统类型就执行/lib/udev/vol_id来取得文件系统类型。 ----------------11.4) 执行local-premount/ 下的脚本: -------------------------11.4.1) 文件resume: (没看太懂) ------------------------------a) 处理传递的resume参数,它指定的是一个分区,格式为resume=LABEL=/dev/sda1或resume=UUID=xxx, 用于suspend to disk ----------------11.5) 根据文件系统类型加载相应的模块,例如如果根文件系统是ext2类型的,就加载ext2(如果没编译进内核)。 ----------------11.6)mount根文件系统到/root上 ----------------11.7) 执行local-bottom/下的脚本(这里没有脚本)。
--------12) 执行init-bottom/下的脚本: ---------------12.1) 文件udev: ------------------------a) kill掉udevd ------------------------b) nuke /dev/.udev/queue/ (不知啥意思) ------------------------c) 执行/etc/udev/udev.conf 设置变量tmpfs_size=10M ------------------------d) mkdir /dev/.static/dev (注意,这个目录不是在硬盘上创建的) ------------------------e) mount -n -o bind /root/dev /dev/.static/dev 把真实文件系统的/dev目录下的node(在硬盘上) bind到/dev/.static/dev/下 ------------------------f) mount -n -o move /dev /root/dev 把tmpfs类型的udev移到真实的文件系统的/dev下,(注意前面一开始就在/dev上mount了udev), 这时真实文件系统的/dev上mount了udev,而原来在硬盘的/dev目录下的node到了/dev/.static/dev下,实际上在硬盘上没有/dev/.static这个目录. (这个不太好理解) ------------------------g) nuke /dev (不知啥意思) ------------------------h) ln -s /root/dev /dev 前面mount -o move之后,目录/dev下什么也没了,这时/root还没真正成为根。这一步使得,如果其它脚本要使用/dev/下的设备文件时不至于找不到。
--------13) mount -n -o move /sys /root/sys
--------14) mount -n -o move /proc /root/proc
--------15) exec run-init /root /sbin/init "$@" /root/dev/console 这一步应该是使真实文件系统成为根,并执行它的/sbin/init(没看懂)
注:加载aic94xx时,需要从目录/usr/lib/hotplug/firmware或者/lib/udev/firmware下读入firmware数据,所以还要建立目录usr/lib/hotplug/firmware或者lib/udev/firmware (在initrd的根目录下,即/boot/myinit) ,然后,把aic94xx-seq.fw拷到任何一个目录中。 可以看出,如果把aic94xx编译进内核,就应把firmware数据也放进内核,但这个功能还没实现。参见内核文档Documentation/firmware_class/README的末尾。
|