[转贴]关于linux下两种内存引导模式的区别
很早以前,为了研究新的ubuntu为什么没有inittab文件,看到了下面这篇烙铁(希望不会被说是火星贴 :-Q)
--------------------------------------------------------
关于linux下两种内存引导模式的区别
2006-08-26 09:20:25
Linux 的 initrd 技术是一个非常普遍使用的机制,linux2.6 内核的 initrd 的文件格式由原来的文件系统镜像文件转变成了 cpio 格式,变化不仅反映在文件格式上, linux 内核对这两种格式的 initrd 的处理有着截然的不同。
initrd 的英文含义是 boot loader initialized RAM disk,就是由 boot loader 初始化的内存盘。在 linux内核启动前, boot loader 会将存储介质中的 initrd 文件加载到内存,内核启动时会在访问真正的根文件系统前先访问该内存中的 initrd 文件系统。在 boot loader 配置了 initrd 的情况下,内核启动被分成了两个阶段,第一阶段先执行 initrd 文件系统中的"某个文件",完成加载驱动模块等任务,第二阶段才会执行真正的根文件系统中的 /sbin/init 进程。这里提到的"某个文件",Linux2.6 内核会同以前版本内核的不同,所以这里暂时使用了"某个文件"这个称呼,后面会详细讲到。第一阶段启动的目的是为第二阶段的启动扫清一切障爱,最主要的是加载根文件系统存储介质的驱动模块。我们知道根文件系统可以存储在包括IDE、SCSI、USB在内的多种介质上,如果将这些设备的驱动都编译进内核,可以想象内核会多么庞大、臃肿。
2.Linux2.4内核对 Initrd 的处理流程
为了使读者清晰的了解Linux2.6内核initrd机制的变化,在重点介绍Linux2.6内核initrd之前,先对linux2.4内核的 initrd进行一个简单的介绍。Linux2.4内核的initrd的格式是文件系统镜像文件,本文将其称为p_w_picpath-initrd,以区别后面介绍的linux2.6内核的cpio格式的initrd。 linux2.4内核对initrd的处理流程如下:
1. boot loader把内核以及/dev/initrd的内容加载到内存,/dev/initrd是由boot loader初始化的设备,存储着initrd。
2. 在内核初始化过程中,内核把 /dev/initrd 设备的内容解压缩并拷贝到 /dev/ram0 设备上。
3. 内核以可读写的方式把 /dev/ram0 设备挂载为原始的根文件系统。
4. 如果 /dev/ram0 被指定为真正的根文件系统,那么内核跳至最后一步正常启动。
5. 执行 initrd 上的 /linuxrc 文件,linuxrc 通常是一个脚本文件,负责加载内核访问根文件系统必须的驱动, 以及加载根文件系统。
6. /linuxrc 执行完毕,真正的根文件系统被挂载。
7. 如果真正的根文件系统存在 /initrd 目录,那么 /dev/ram0 将从 / 移动到 /initrd。否则如果 /initrd 目录不存在, /dev/ram0 将被卸载。
8. 在真正的根文件系统上进行正常启动过程 ,执行 /sbin/init。 linux2.4 内核的 initrd 的执行是作为内核启动的一个中间阶段,也就是说 initrd 的 /linuxrc 执行以后,内核会继续执行初始化代码,我们后面会看到这是 linux2.4 内核同 2.6 内核的 initrd 处理流程的一个显著区别。
回页首
3.Linux2.6 内核对 Initrd 的处理流程
linux2.6 内核支持两种格式的 initrd,一种是前面第 3 部分介绍的 linux2.4 内核那种传统格式的文件系统镜像-p_w_picpath-initrd,它的制作方法同 Linux2.4 内核的 initrd 一样,其核心文件就是 /linuxrc。另外一种格式的 initrd 是 cpio 格式的,这种格式的 initrd 从 linux2.5 起开始引入,使用 cpio 工具生成,其核心文件不再是 /linuxrc,而是 /init,本文将这种 initrd 称为 cpio-initrd。尽管 linux2.6 内核对 cpio-initrd和 p_w_picpath-initrd 这两种格式的 initrd 均支持,但对其处理流程有着显著的区别,下面分别介绍 linux2.6 内核对这两种 initrd 的处理流程。
cpio-initrd 的处理流程
1. boot loader 把内核以及 initrd 文件加载到内存的特定位置。
2. 内核判断initrd的文件格式,如果是cpio格式。
3. 将initrd的内容释放到rootfs中。
4. 执行initrd中的/init文件,执行到这一点,内核的工作全部结束,完全交给/init文件处理。
p_w_picpath-initrd的处理流程
1. boot loader把内核以及initrd文件加载到内存的特定位置。
2. 内核判断initrd的文件格式,如果不是cpio格式,将其作为p_w_picpath-initrd处理。
3. 内核将initrd的内容保存在rootfs下的/initrd.p_w_picpath文件中。
4. 内核将/initrd.p_w_picpath的内容读入/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。
通过上面的流程介绍可知,Linux2.6内核对p_w_picpath-initrd的处理流程同linux2.4内核相比并没有显著的变化, cpio-initrd的处理流程相比于p_w_picpath-initrd的处理流程却有很大的区别,流程非常简单,在后面的源代码分析中,读者更能体会到处理的简捷。
4.cpio-initrd同p_w_picpath-initrd的区别与优势
没有找到正式的关于cpio-initrd同p_w_picpath-initrd对比的文献,根据笔者的使用体验以及内核代码的分析,总结出如下三方面的区别,这些区别也正是cpio-initrd的优势所在:
cpio-initrd的制作方法更加简单
cpio-initrd的制作非常简单,通过两个命令就可以完成整个制作过程
#假设当前目录位于准备好的initrd文件系统的根目录下
bash# find . | cpio -c -o > ../initrd.img
bash# gzip ../initrd.img
而传统initrd的制作过程比较繁琐,需要如下六个步骤
#假设当前目录位于准备好的initrd文件系统的根目录下
bash# dd if=/dev/zero of=../initrd.img bs=512k count=5
bash# mkfs.ext2 -F -m0 ../initrd.img
bash# mount -t ext2 -o loop ../initrd.img /mnt
bash# cp -r * /mnt
bash# umount /mnt
bash# gzip -9 ../initrd.img
本文不对上面命令的含义作细节的解释,因为本文主要介绍的是linux内核对initrd的处理,对上面命令不理解的读者可以参考相关文档。
注:initrd.img和bootsplash共存的方式:
QUOTE:
Okay, so it seems this is completely wrong. What is required is to copy the splash p_w_picpath file to the rootfs of your initramfs. You don't need to append the p_w_picpath to the compressed initramfs at all. So the correct process is actually:
Build busybox and install it into a directory. We'll call it /mnt/mythbox/busybox.
1. Create a splash data file using a command like this: splash -s -f <path to splash config file> > splashfile
2. Copy splashfile to the top level of your busybox directory: cp splashfile /mnt/mythbox/busybox/bootsplash Note that the filename in the busybox directory must be bootsplash.
3. Now build your compressed cpio p_w_picpath that will be your 2.6 initramfs. You don't need to append the splash file to the compressed initramfs.
Now it works. This makes sense, too, if you read the bootsplash patch in the init() function. It tries to open the file "/bootsplash". I can see how people thought you still needed to append the file because that's what you did with 2.4. But I'm not sure why the information about placing the file in the root of the rootfs isn't posted somewhere else.
Anyway, this seems to work now. I've verified it with several builds. 如果要用cpio-initrd代替p_w_picpath-initrd
可以考虑用 mkinitcpio 代替 mkinitrd
mkinitcpio的下载地址:
http://phraktured.net/initramfs
已经有发行版本这样作了
http://www.archlinux.org/news/240/
[ 本帖最后由 lanzinc 于 2007-7-29 23:28 编辑 ] 一个是内核启动中的执行,一个是内核启动后执行。
就这个区别? 我认为:
主要是,根文件系统的加载,内核模块加载,和内核加载后执行流的转移几方面的不同。
执行流,文章作者描述得还算清楚。
当然文件格式和制作也不同。 现在好像只有arch使用了mkinitcpio,我曾经想移植到mgc上,不过需要改动的东西太多,最后放弃了。 我觉得视乎改变的不多,
initrd启动后,仍然是启动真正的根系统上的init,加载inittab,根据他进行进一步的动作。
改变的只有initrd本身。
一个arch initrd的样子
========================
----------------archlinux initrd.img 的目录树---------------------------------
|-- bin
| |-- cat
| |-- chroot
| |-- cpio
| |-- dd
| |-- false
| |-- fstype
| |-- gunzip
| |-- gzip
| |-- halt
| |-- insmod
| |-- ipconfig
| |-- kill
| |-- kinit
| |-- kinit.shared
| |-- ln
| |-- losetup
| |-- mdassemble
| |-- minips
| |-- mkdir
| |-- mkfifo
| |-- mknod
| |-- moddeps
| |-- modprobe -> /sbin/modprobe
| |-- mount
| |-- nfsmount
| |-- nuke
| |-- parseblock
| |-- pivot_root
| |-- poweroff
| |-- readlink
| |-- reboot
| |-- replace
| |-- resume
| |-- run-init
| |-- sh
| |-- sleep
| |-- true
| |-- umount
| |-- uname
| `-- zcat
|-- config
|-- dev
|-- etc
| |-- start_udev
| `-- udev
| |-- rules.d
| | `-- udev.rules
| `-- udev.conf
|-- hooks
| |-- filesystems
| `-- udev
|-- init
|-- kernel26
|-- lib
| |-- klibc-rOj3PRLKBA9FcF5ZuoqKQLmOWcA.so
| |-- modules
| | `-- 2.6.22-ARCH
| | |-- kernel
| | | |-- drivers
| | | | |-- cdrom
| | | | | `-- cdrom.ko
| | | | `-- ide
| | | | |-- ide-cd.ko
| | | | |-- ide-core.ko
| | | | |-- ide-disk.ko
| | | | `-- pci
| | | | |-- generic.ko
| | | | `-- via82cxxx.ko
| | | `-- fs
| | | |-- ext3
| | | | `-- ext3.ko
| | | |-- jbd
| | | | `-- jbd.ko
| | | `-- mbcache.ko
| | |-- modules.alias
| | |-- modules.dep
| | `-- modules.symbols
| `-- udev
| |-- ata_id
| |-- cdrom_id
| |-- edd_id
| |-- firmware.sh
| |-- load-modules.sh
| |-- scsi_id
| |-- usb_id
| `-- vol_id
|-- proc
|-- sbin
| |-- modprobe
| |-- udevd
| |-- udevsettle
| `-- udevtrigger
`-- sys
21 directories, 74 files
-------------------------init @ initrd.img -----------------------------------------------------
#!/bin/sh
msg () { [ "${quiet}" != "y" ] && echo $@; }
err () { echo "ERROR: $@"; }
msg ":: Loading Initramfs"
/bin/mount -t sysfs none /sys
/bin/mount -t procnone /proc
read CMDLINE </proc/cmdline
export CMDLINE
# Used so hooks can override params to kinit
export kinit_params=""
export root=""
echo "/sbin/modprobe" > /proc/sys/kernel/modprobe
for cmd in $CMDLINE; do
case "$cmd" in
\#*) break ;; # ignore everything after a # in the commandline
) export runlevel="$cmd" ;;
single) export runlevel="S" ;; #some people use 'single'
# replace can cause problems for the following entries
# These should only be applied to the lefthand side of the expression
# until we find a fix hardcode the stuff here.
root=*) export "${cmd}";;
md=*) export "${cmd}" ;;
crypto=*) export "${cmd}" ;;
resume2=*) export "${cmd}" ;;
ip=*) export "${cmd}" ;;
nfsaddrs=*) export "${cmd}" ;;
nfsroot=*) export "${cmd}" ;;
# only export stuff that does work with dash :)
*=*) cmd="$(replace "${cmd}" '.' '_')"
cmd="$(replace "${cmd}" '-' '_')"
export "${cmd}"
;;
*) cmd="$(replace "${cmd}" '.' '_')"
cmd="$(replace "${cmd}" '-' '_')"
export "${cmd}=y"
;;
esac
done
if [ "x${disablehooks}" != "x" ]; then
for d in $(replace "${disablehooks}" ','); do
export "hook_${d}=disabled"
done
fi
if [ "x${disablemodules}" != "x" ]; then
for d in $(replace "${disablemodules}" ','); do
export "mod_${d}=disabled"
done
fi
if [ "x${earlymodules}" != "x" ]; then
for m in $(replace "${earlymodules}" ','); do
/sbin/modprobe -q $m > /dev/null 2>&1
done
fi
. /config
for m in $MODULES; do
TST=""
eval "TST=\$mod_${m}"
if [ "${TST}" != "disabled" ]; then
/sbin/modprobe -q $m > /dev/null 2>&1
fi
done
if [ -e "/hooks" ]; then
for h in $HOOKS; do
TST=""
eval "TST=\$hook_${h}"
if [ "${TST}" != "disabled" ]; then
run_hook () { msg "$h: no run function defined"; }
if [ -e "/hooks/$h" ]; then
. /hooks/$h
msg ":: Running Hook [${h}]"
run_hook
fi
fi
done
fi
if [ "${rootdelay}" != "0" ]; then
msg -n "Waiting for devices to settle..."
/bin/sleep "${rootdelay}"
export rootdelay=0
export kinit_params="$kinit_params rootdelay=0"
msg "done."
fi
if [ "${break}" = "y" ]; then
echo ":: Break requested, type 'exit' to resume operation"
echo " NOTE: klibc contains no 'ls' binary, use 'echo *' instead"
PS1="ramfs$ " /bin/sh -i
fi
if [ ! -b "${root}" ]; then
# This duplicates code from the filesystem hook
# without this, mkinitcpio would fail for users who use
# neither the udev hook, nor the filesystem hook
msg "\nRoot device '${root}' doesn't exist, attempting to create it"
eval $(/bin/parseblock "${root}")
if [ "${BLOCKNAME}" = "unknown" ]; then
echo "ERROR: Failed to parse block device name for '${root}'"
elif [ "x${BLOCKDEVICE}" = "x" ]; then
echo "ERROR: Failed to parse block device ids for '${root}'"
else
export root="${BLOCKNAME}"
echo "/bin/mknod \"${BLOCKNAME}\" b ${BLOCKDEVICE}"
/bin/mknod "${BLOCKNAME}" b ${BLOCKDEVICE} >/dev/null
fi
if [ ! -b "${root}" ]; then
err "Unable to create/detect root device '${root}'"
echo "Dropping to a recovery shell... type 'exit' to reboot"
echo "NOTE: klibc contains no 'ls' binary, use 'echo *' instead"
echo ""
echo "If the device '${root}' gets created while you are here,"
echo "try adding 'rootdelay=8' or higher to the kernel command-line"
PS1="ramfs$ " /bin/sh -i
msg "Rebooting..."
/bin/reboot
fi
fi
msg ":: Initramfs Completed - control passing to kinit"
if [ -f "/message" ]; then
msg "$(cat /message)"
fi
#Special handling if udev is running
udevpid=$(/bin/minips -C udevd -o pid=)
if [ "x${udevpid}" != "x" ]; then
/bin/kill -9 $udevpid
/bin/sleep 0.01
fi
exec /bin/kinit -- "root=${root}" ${kinit_params} "${runlevel}" > /dev/null 2>&1
============================================================
而且我将生成的arch 的 initrd.img用到mgc上是没问题的。
因此生成的initrd.img应该是没问题的。
只是改生成initrd的工具而已
以上是我不太成熟的看法。
替换后对硬件的支持应该能够变好。
[ 本帖最后由 lanzinc 于 2007-8-16 11:36 编辑 ]
页:
[1]