#!
/bin/ash
echo
echo“Simpleinitrdisactive”
echo
mount-tproc/proc/proc
mount-tsysfsnone/sys
/bin/ash–login
EOF
chmod+x/mnt/initrd/linuxrc
#Finishup…
umount/mnt/initrd
gzip-9/tmp/ramdisk.img
cp/tmp/ramdisk.img.gz/boot/ramdisk.img.gz
想创建initrd的话,你需要首先创建一个空文件,将/dev/zero(0字符流)做为ramdisk.img的输入。
得到的文件大小大约是4MB(有4000个1K的块组成)。
接下来,用mke2fs命令来创建一个使用这个空文件的ext2文件系统。
现在,这个文件就是一个ext2文件系统。
ok,接下来,以回路设备的形式挂载这个文件到/mnt/initrd,现在,你就在挂载点拥有一个代表着ext2文件系统的目录,并用与存放你的initrd。
其他大多数的脚本语句都是用于实现这个功能。
下一步,就是创建一些必须的子目录,用于生成你的根文件系统:
/bin,/sys,/dev,和/pro。
这里只需要少数几个目录,例如,没有/lib。
但是它们已经包含了大部分功能。
如果想让你的根文件系统发挥更大的作用,请使用BusyBox。
这个工具是一个包含了许多独立工具的镜像,这些独立的工具你都能在linux中找到(ash,a等等wk,sed,insmod)。
BusyBox的优势在于,它把它们集合在了一起,并分享了公用的部分,从而极大缩小了镜像的体积。
这对于嵌入式系统来讲,是非常理想的。
请将BustBox镜像从它的源目录中复制出来,到你的/bin目录下,这样,很多指向BusyBox工具集的符号链接将被创建,BusyBox能确定哪一个工具将被使用,并自动引用它。
这个/bin目录下被创建的链接的小型集合将用于对启动脚本的支持。
再下一步,就是一小部分特殊设备文件的创建。
我从我的/dev文件夹中直接拷贝了出来,别忘了加上-a选项来保持它们原有的属性。
倒数第二步,就是生成linuxrc文件。
在内核挂载了内存盘之后,它将搜索并执行相关的启动文件,如果没有找到,内核就将linuxrc文件做为其启动脚本。
你最好在这个文件中对环境变量做一些基本设置,例如挂载/proc文件系统等。
除了/proc外,我还挂载了/sys文件系统,将消息发送给终端。
最后,我调用ash并通过它和根文件系统交互。
最后记住,用chmod把linuxrc文件的属性改为可执行。
最后,你的根文件系统算是ok了。
现在它并没有被挂载,用gzip将它压缩,并将压缩后的文件ramdisk.img.gz拷贝到/boot目录下,这样它就能被GRUB调用。
想要构建你的初始化ram盘的话,你只需要调用mkird,镜像就将自动创建并拷贝到/boot目录下。
测试自定义的初始化RAM盘
你拥有的新的initrd镜像是在/boot目录下,因此,下一步就是要用你默认的内核来测试它。
ok,现在你可以先重新启动你的linux系统,当GRUB引导画面出现时,按下C键,打开GRUB的命令行工具。
现在,你就能通过GRUB确定启动专门的内核和initrd镜像。
内核命令是允许你定制内核文件的,而initrd命令则允许你指定专门的initrd镜像文件。
当它们都被指定之后,通过启动命令来启动内核,如下所示:
GNUGRUBversion0.95(638Klower/97216Kuppermemory)
[MinimalBASH-likelineeditingissupported.Forthefirstword,TAB
listspossiblecommandcompletions.AnywhereelseTABliststhepossible
completionsofadevice/filename.ESCatanytimeexits.]
grub>kernel/bzImage-2.6.1
[Linux-bzImage,setup=0×1400,size=0×29672e]
grub>initrd/ramdisk.img.gz
[Linux-initrd@0×5f2a000,0xb5108bytes]
grub>boot
UncompressingLinux…OK,bootingthekernel.
在内核启动之后,它开始检查initrd镜像是否可用,如果答案是确定的,那么就作为根文件系统加载并挂载它。
下面就是这个特殊启动过程的结尾:
…
md:
AutodetectingRAIDarrays
md:
autorun
md:
…autorunDONE.
RAMDISK:
Compressedimagefoundatblock0
VFS:
Mountedroot(ext2filesystem).
Freeingunusedkernelmemory:
208kfreed
/$ls
binetclinuxrcprocsys
devliblost+foundsbin
/$cat/proc/1/cmdline
/bin/ash/linuxrc
/$cdbin
/bin$ls
ashcatechomountsysctl
busyboxdmesglsps
/bin$touchzfile
/bin$ls
ashcatechomountsysctl
busyboxdmesglspszfile
当启动之后,可以通过ash来进入命令模式。
在本例中,我探究了根文件系统并向你演示了,你能通过新建文件来写入这个文件系统。
只需要注意,第一步是要创建linuxrc。
通过初始化内存盘启动
现在,大家已经看到了如何构建并使用一个自定制的初始化内存盘,这一节则用于介绍,内核是如何辨认initrd并将其作为它的根文件系统挂载的。
我将涉及一些bootchain中的主要的函数并对发生的事件做出解释。
像GRUB这样的bootloader,通常会确认即将加载的内核并复制该内核镜像与任何相关联的initrd到内存中,你可以在你linux内核源程序目录下的./init子目录中找到这些功能实现。
在内核与initrd镜像被解压缩和复制到内存后,内核被调用。
此时,开始各种各样的初始化过程,最终,你会发现自己处于init/main.c:
init()(subdir/file:
function)。
这个函数实现了很多的子系统初始化。
在这里,要调用init/do_mounts.c:
prepare_namespace(),用来准备命名空间(挂载dev文件系统,RAID,或者md,devices,以及,最后的initrd)。
通过对init/do_mounts_initrd.c:
initrd_load()的调用,最终完成对initrd的加载。
initrd_load()调用init/do_mounts_rd.c:
rd_load_image(),来决定是否通过调用init/do_mounts_rd.c:
identify_ramdisk_image()来加载内存盘镜像。
后面这个函数通过检查内核的编号来确定文件究竟是是minux,etc2,romfs,cramfs,还是gzip格式,直到返回initrd_load_image后,init/do_mounts_rd:
crd_load()又被调用。
这个函数负责分配空间给内存盘,并进行校验计算,解压缩,最后将内存盘镜像加载到内存中。
此时,你就已经拥有了一个适合于挂载的,在块设备中的initrd镜像。
现在,通过调用init/do_mounts.c:
mount_root()将这个块设备做为root挂载。
ok,根设备就被创建了,接下来调用的函数是init/do_mounts.c:
mount_block_root(),此函数又调用fs/namespace.c:
sys_mount()来挂载真实的根文件系统并对其进行chdir操作。
最后,会返回到启动函数中,并调用init/main.c:
run_init_process。
调用的结果是,初始化进程开始(在这里是通过/linuxrc)。
linuxrc可以是一个可执行程序,也可以是脚本(只要脚本解释器能够正常解释它)。
函数调用的层次关系可以从下表中看出。
并不是所有与复制、挂载初始化内存盘的函数都被列举出来,这里仅仅是大概的,对整体基本流程的回顾:
init/main.c:
init
init/do_mounts.c:
prepare_namespace
init/do_mounts_initrd.c:
initrd_load
init/do_mounts_rd.c:
rd_load_image
init/do_mounts_rd.c:
identify_ramdisk_image
init/do_mounts_rd.c:
crd_load
lib/inflate.c:
gunzip
init/do_mounts.c:
mount_root
init/do_mounts.c:
mount_block_root
init/do_mounts.c:
do_mount_root
fs/namespace.c:
sys_mount
init/main.c:
run_init_process
execve
无盘启动的应用
同很多嵌入式系统的启动一样,本地磁盘(软驱或者光驱)对于启动内核和内存盘根文件系统来说,并不是必须的。
DHCP工具能被用于确认网络参数,例如大家熟悉的IP抵制和子网掩码等。
此外,TFTP能被用于将内核镜像以及初始化内存盘镜像传送到本地设备。
一旦传输完成,linux内核就能被启动以及挂载initrd,和本地镜像启动的过程一样。
让你的initrd尽可能小
当你在构建嵌入式系统时,总是希望initrd的镜像尽可能小,恩,这里将提供一些小技巧。
首先就是使用BusyBox。
前面已经提到过,BusyBox包含了很多较大的工具,通常体积都以MB计算,但是它成功得将自己的体积控制在几百KB的范围内。
在本例中,BusyBox镜像使用的是静态链接,因此不需要提供任何库文件。
但是,如果你需要得到标准的C库文件来满足自己的二进制程序,除了大体积的glibc库,你有其他更好的选择。
第一个,小体积的uClibc库,是专门用于有空间限制的,标准C库的缩水版本。
另一个适用于有空间限制环境的库是dietlib。
记住,你需要在自己的嵌入式系统中,用这些库重新编译你的二进制程序。
虽然使用它们会带来一些附加的工作,但是,是值得的。
总结
初始化内存盘技术被创建的最初目的,是为了让内核通过一个临时的根文件系统来过渡到最终的根文件系统。
initrd对于嵌入式linux系统同样是很有用处的:
它能做为一个非持续性的根文件系统挂载到内存盘中。