跟我一步一步制作一个基本的linux启动盘

       原创文章,转载请注明出处,谢谢!       
       作者:清林,博客名:飞空静渡

 

这是一篇自己很早写的文章了,当时根据网上的一些教程一步一步制作,本来想把自己做的过程放到网上,可以西一值忙于公司项目,没有时间,去年想放上来的时候发现csdn上传不了图片,我想,没有图片的教程就不叫一步一步的教程了。好了,下面是我一步一步制作的linux的简单的启动盘,复杂一些的以后再写吧!

 

本文将会一步一步从基础引导大家构建一个linux启动盘,在这个linux启动盘在启动后可以运行我们特定的程序,完成特定的功能。本文力求简单详细,写出制作的每一步,其中会穿插各种图片,在引用其他文章时,会把文章贴出来,使得读者不用去链接寻找,也同时使得没有网络的读者可以省去无网络之麻烦。使得初学者可以照着一步一步的完成,   
    本着尊重原创的精神,文章中如果没有特殊说明,则所有斜体文字都引自其他文章的内容,其中大部分来自《精通initramfs构建step by step》。
    注:本文参考的主要两篇文章是《精通initramfs构建step by step》和《深入理解 Linux 2.6 的 initramfs 機制 (上)》,文章如果引用到其他文章则在文中说明。
    工作环境:ubuntu9.10,练习用的内核是linux-2.6.31.tar.bz2,ubuntu9.10默认的是gcc4.4,我用的是gcc4.3.4(具体修改gcc版本可用update-alternatives命令)。

一、制作一个显示hello world的启动linux

    一、 initramfs 是什么
    在 2.6 版本的 linux 内核中,都包含一个压缩过的 cpio 格式 的打包文件。当内核启动时,会从这个打包文件中导出文件到内核的 rootfs 文件系统,然后内核检查 rootfs 中是否包含有 init 文件,如果有则执行 它,作为 PID 为 1 的第一个进程。这个 init 进程负责启动系统后续的工作,包括定位、挂载“真正的”根文件系统设备(如果有的话)。如果内核没有在 rootfs 中找到 init 文件,则内核会按以前版本的方式定位、挂载根分区,然后执行/sbin/init 程序完成系统的后续初始化工作。
这 个压缩过的 cpio 格式的打包文件就是 initramfs。编译 2.6 版本的 linux 内核时,编译系统总会创建 initramfs,然后把它与编译好的 内核连接在一起。内核源代码树中的 usr 目录就是专门用于构建内核中的 initramfs 的,其中的 initramfs_data.cpio.gz 文件就 是 initramfs。缺省情况下, initramfs 是空的,X86 架构下的文件大小是 134 个字节。

   
    我在做练习时,没有在内核源代码树usr中找到initramfs_data.cpio.gz,倒是有个initramfs_data.cpio文件,不过也没关系,initramfs_data.cpio文件就可以了。

    二、构建第一个 initramfs : hello world
    从 C 语言开始,学习计算机编程语言的第一个程序几乎都是 hello world,因此我们也构建一个最简单 的 hello world 式的 initramfs,以说明 initramfs 的基本构建方法。 initramfs 的灵魂是 init 文件(或者叫程序,因为它会被内核第一个执行),我们先写一个简单的 init 程序,它会在内核的 console 中打印出经典的 hello world 信息。
hello.c:

其中的 sleep()函数语句是为了避免执行时内核很快打出 panic 的信息,并非功能上的需要。
接着把 hello.c 编译成静态连接程序:
gcc -o hello_static -static -s hello.c
命令行中的-s 参数表示编译后的程序不包含调试定位信息,目的是减少编译出来的程序文件的大小。
再创建一个 initramfs 的构建源文件目录 image,把 hello_static 程序拷入这个目录,并改名为
init。

    在 image 目录下,创建一个 dev/console 的设备文件,否 init 程序无法在内核 console 中输出信息:
mknod -m 600 dev/console c 5 1
注意,执行这个命令需要有 root 权限。
   
    我的练习目录是这样的/home/fjb/home/test/initramfs/t1。我在t1目录下建立了image目录。其中sudo mkdo -m 600 dev/console c 5 1 这个命令是创建一个设备文件,-m mode, --mode=mode 为新建立的文件设定模式,就象应用命令chmod一样,以后仍然使用缺省模式建立新目录。    dev/console是要创建的设备,即一个控制台设备,我们后面在启动linux时其显示的信息会用上这个控制台,我们可以在image目录下先建立dev目录(mkdir dev),c表示是字符设备,5和1是主次版本号。注意在ubuntu上要用超级用户权限才可以建立。
    这样在t1目录下就有了hello.c、hello_static文件和image目录,在image目录里有init文件和dev目录,在dev目录下有console设备文件。

    好,现在可以构建initramfs了。
    首先是配置linux。我把linux-2.6.31.tar.bz2拷贝到/home/fjb/test/initramfs/目录下,运行tar -xvf linux-2.6.31.tar.bz2进行解压,在initramfs目录下生成一个linux-2.6.31目录,里面有linux的源代码(linux的下载可以在http://kernel.org/pub/linux/kernel/ 的2.6目录里下载)。

    进入linux-2.6.31目录,运行make menuconfig,会出现如下图的配置画面:

    在general setup的配置目录下的选上Initial RAM filesystem and RAM disk (initramfs/initrd) support选项,选上的话,旁边的中括号会有个*,然后在下面输入/home/fjb/home/test/initramfs/t1/image,如下图:


       
     因为我们的 init 程 序是 ELF 格式的,所以内核需要支持ELF 的可执行文件,否则启动这个 init 程序会失败。在内核的 Executable file formats 配置目录下,选择 kernel support for ELF binaries,则可使内核支持 ELF 格式的可执行文件。其他内核 配置参数根据实际需要设置即可,不过,为了减少内核编译时间,可参考这篇文章
http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-
293122.html 设置一个最简单的内核配置。

    为了节省大家的时间,我把这篇文章拷贝到下面(linuxman的博客,很不错,荐):

=============================================
X86_64-Pure64 CLFS实践笔记(二)

三、适合CLFS临时系统的最简内核配置
我的host系统是32位的,无法使用chroot方式构建CLFS系统,只能先交叉编译出一个最小的临时系统,然后以BOOT方式完成CLFS系统的构建。在CLFS BOOK中,对临时系统的内核的配置有下面的警告:
Warning
Here a temporary cross-compiled kernel will be built. When configuring it, select the minimal amount of options required to boot the target machine and build the final system. I.e., no support for sound, printers, etc. will be needed.
Also, try to avoid the use of modules if possible, and don't use the resulting kernel image for production systems.

那么,怎样得到一个适合CLFS临时系统的最简内核配置呢?

(1)首先,用内核的 allnoconfig 配置目标,得到一个最最基本的内核配置。即,执行下面的命令:
make ARCH=x86_64 CROSS_COMPILE=${CLFS_TARGET}- allnoconfig
内核的 allnoconfig 配置目标会把所有的内核选项都设置为no,也就是把它们既不编译进内核,也不编译成模块。有了这个最基本的配置,我们再添加CLFS临时系统所必须的配置项:再执行
make ARCH=x86_64 CROSS_COMPILE=${CLFS_TARGET}- menuconfig
命令,按下面的步骤添加其他的配置——

(2)按CLFS BOOK的建议,把 Executable file formats 下的ELF 和 emulations for 32bit ELF 选项编译进内核。

(3)在 Processor type and features 下面,选择合适的CPU类型。例如,我的CPU类型是Intel EM64T

(4)选择PCI/PCI-Express支持,位于Bus options (PCI, PCMCIA, EISA, MCA, ISA) 配置目录下。

(5)加入对根文件系统所在磁盘控制器的驱动,详细方法可参考:http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-280649.html。例如,我的机器的配置是:
Device Driver
|---->SCSI device support
|---->SCSI disk support
|----->SCSI low-level drivers
  |---->Serial ATA (SATA) support
  |---->intel PIIX/ICH SATA support

(6)加入Ext2文件系统的支持:在 File systems 配置目录下,选择 Second extended fs support。如果根文件系统是Ext3,则选择 Ext3 journalling file system support。

(7)为了是 Udev 正常工作,需要内核支持 Unix domain sockets。此配置选项位于 Networking 配置目录中的 Networking support ---> Networking options 下。

(8) 使内核支持 /proc 虚拟文件系统和 tmpfs 文件系统: File systems ---> Pseudo filesystems ---> /proc file system support / Virtual memory file system support (former shm fs)

(9)支持 swap 分区: General setup ---> Support for paging of anonymous memory (swap)

(10)支持 RTC 设备: Device Drivers ---> Character devices ---> Enhanced Real Time Clock Support

(11) 为了充分发挥我的双核CPU的能力,我又加入了对SMP的支持: Processor type and features ---> Symmetric multi-processing support。这一特性,对CLFS系统来说,并不是必须的。

这样一个最简的内核配置,编译出来的内核映像文件的大小只有744KB,但却能完全满足CLFS系统后续的构建工作需要。

=============================================

    由于我的31内核配置和他的有些不一样,我就把sata和ata硬盘等的选项都选上了,(第一次制作,还不清楚很多选项,没办法了)。
    之后我们就可以保存选项,然后运行make bzImage之后在运行make就ok了(编译比较长时间,慢慢等吧,我没有咖啡泡,只能跑去吃饭了)。

二、实验环境的搭建
    试验 initramfs 需要经常重启系统,所以使用 CPU 模拟器是不错的选择。我们可以选用 qemu,它支持直接启动 linux 内核,无需在模拟器中安装 OS。   
    如果在在命令行上用qemu的话会有问题,那是qemu的一个bug,对于27后的内核支持不好,在运行后会出现apic的错误,如果要用命令行上用qemu的话,运行qemu -kernel linux-2.6.31/arch/i386/boot/bzImage -append noapic -hda /dev/zero可以禁止内核的apic。这样就不会有这个问题。但这里有个更好的方法是用Qemu Luancher,既简单又方便好用。
    Qemu的安装可参考Linuxman的文章《在Linux系统下安装图形化的QEMU》,链接:http://linuxman.blog.ccidnet.com/blog-htm-do-showone-uid-60710-type-blog-itemid-612280.html。同样为了大家的方便,我把他的文章贴在下面(摆明是力求详细嘛)。


================================================================================
在Linux系统下安装图形化的QEMU
QEMU是一款经典的CPU模拟器软件,但它是命令行方式程序,参数众多,使用起来不是很方便。QEMU有一个图形化的前端程序qemu-laucher,可方便地设置QEMU的命令行参数。
我的系统是Debian 4.0 Amd64,软件库中已包含了qemu-laucher,可以方便地安装。安装完整的图形化QEMU环境,需要安装下面的软件包:
qemu:QEMU主程序
qemu-launcher:图形化的QEMU启动器
kqemu-common:QEMU的加速模块程序
qemuctl:QEMU的运行时选项的设置程序,可作为qemu-laucher 的插件程序。
安装完成后,在 Application/Accessories 系统菜单下,添加了qemu-laucher 的启动项。

我还有一个openSuse 10.3 的系统,遗憾的是,它的软件库中没有包含qemu-laucher,无法直接安装。
================================================================================
   
    ubuntu就方便了,在新立得里就可以把这些全装上了。装完后会生成一个菜单:应用程序-》附件-》Qemu Launcher。点击一个菜单启动 Qemu Launcher,如下图。


    其中,由于我们在启动linux时需要硬盘,虽然我们不是必须,但为了启动不出错,我们可以New一个disk出来,如上图的Hand disk 0,我建了一个100M的虚拟硬盘t1。
    然后点击linux boot选项,勾上Boot Linux kernel directly选项,在下面的Kernel image中找到我们刚才生成的bzImage文件,在 linux-2.6.31/arch/i386/boot/bzImage文件夹里。在Initial RAM disk选择中选择我们刚才生成的那个硬盘。好了,到此我们已可以启动我们刚才建立的linux了,点击Launch可以运行我们的linux了(记得点击Save按钮,不然下次启动Qemu Launcher时又的重新去找那些文件)。
    内核输出一堆内核运行信息后,最后打出了 hello world, from initramfs。如下图:



三、什么是initramfs

      1.initramfs 的前世今生
      1.1什么是 rootfs 和 ramfs 所有的 2.6 版本 linux 内核都有一个特殊的文件系统 rootfs,是内核启动的初始始根文件系统, initramfs 的文件会复制到 rootfs。如果把 initramfs 比作种子,那么 rootfs 就是 它生长的土壤。大部分 linux 系统正常运行后都会安装另外的文件系统,然后忽略 rootfs。rootfs 是 ramfs 文件系统的一个特殊实例。ramfs 是一种非常简单的文件系统,是基于内存的文件系统。ramfs 文件系统没有容量大小的限制,它可以根据需要动态增加容量。ramfs 直接利用了内核的磁盘高速缓存机制。所有的文件的读写数据都会在内存中做高速缓存(cache),当系统再次使用文件数据时,可以直接从内存中读写,以提 供系统的 I/O 性能。高速缓存中的写入数据会在适当的时候回写到对应的文件系统设备(如磁盘等)中,这时它的状态就标识为clean,这样系统在必要时可 以释放掉这些内存。ramfs 没有对应文件系统设备,所以它的数据永远都不会回写回去,也就不会标识为 clean,因此系统也永远不会释放 ramfs 所占 用的内存。因为 ramfs 直接使用了内核已有的磁盘高速缓存机制,所以它的实现代码非常小。也由于这个原因, ramfs 特性不能通过内核配置参数删除,它是内核的天然特性。
    1.2ramfs 不是 ramdisk
ramdisk 是在一块内存区域中创建的块设备,用于存放文件系统。ramdisk 的容量是固定的,不能象
ramfs 一样动态增长。ramdisk 需要内核的文件系统驱 动程序(如 ext2)来操作其上的数据,而ramfs 则是内核的天然特性,无需额外的驱动程序。ramdisk 也象其他文件系统设备一样,需要在块设
备和 内存中的磁盘高速缓存之间复制数据,而这种数据复制实际不必要的。
       1.3从 ramfs 派生的文件系统 tmpfs
ramfs 的一个缺点是它可能不停的动态增长直到耗尽系统的全部内存,所以只有 root 或授权用户允许
使用 ramfs。为了解决这个问题,从 ramfs 派生出了 tmpfs 文件系统,增加了容量大小的限制,而且允
许把数据写入交换分区。由于增加了这两个特性,所以 tmpfs 允许普通用户使用。关于 tmpfs 文件系统更多的信息,可以看内核源码中的 Documentation/filesystems/tmpfs.txt 文档。
    综上所述,initramfs 是一种 ramfs 文件系统,在内核启动完成后把它复制到 rootfs 中,作为内核初始的根文件系统,它的任务是挂载系统真正的根文件系统。这就是 initramfs 的前世今生。


四、使用busybox构建
    busybox 号称是嵌入式 Linux 中的瑞士军刀——小巧、功能齐 全。它把许多常用的 Linux 命令都集成到一个单一的可执行程序中,只用这一个可执行程序(即 busybox)加上 Linux 内核就可以构建一个基本的 Linux 系统。busybox 程序非常小巧,包含全部命令可执行文件大小也只有 750 多。busybox 是完全模块化的,可以很容易地在编译时增加、 删除其中包含的命令。
    由于 busybox 的这些特点,它广泛应用于 LiveCD、应急修复盘、安装盘等系统中。我们也是以它为基础,构建 initramfs 。   


4.1编译busybox
    我们可以去http://busybox.net 上下载最新的busybox源码,我在上面下载的是最新的busybox-1.15.2.tar.bz2版本。把它解压到initramfs目录下,可以用make menuconfig进行配置,另外两个配置命令是
make allyesconfig——最大配置
make allnoconfig——最小配置
    这里用 make defconfig 做缺省配置。

     用make CONFIG_PREFIX=<安装目录> install 命令安装。如果在命令行中省略 CONFIG_PREFIX 变量的赋值,则会安装缺省值 ./_install 目录下。CONFIG_PREFIX 可以在 make menuconfig 的配置界面中修改。
    在busybox-1.15.2目录下运行
    make defconfig
    make CONFIG_PREFIX=/home/fjb/home/test/initramfs/t1/image install
    那么我们就可以把busybox安装在image目录下了,image目录下会多出几个文件夹和文件,如下图:



       缺省配置下,busybox 动态链接到 glibc,所以要把它用到的动态库复制到 initramfs 的构建目录中。用 ldd 命令查看 busybox 用到了哪些动态库文件及相应的文件路径,然后把它们复制到相应的目录下即可。
    在image/bin 目录下,我们运行
    ldd busybox
    得到的结果如下:
    linux-gate.so.1 =>  (0x00137000)
    libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0x009ce000)
    libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00138000)
    /lib/ld-linux.so.2 (0x00f04000)
    我们可以在image下建立lib目录,把这几个苦拷贝到lib目录下。
    这里要注意了,我在这里就浪费了很多时间。
    首先: linux-gate.so.1这个库我们是找不到的,因为linux下根本没有这个库,具体来说就google一下咯,上面有解释,一句话,这个不需要。
     libm.so.6、 libc.so.6和/ld-linux.so.2这三个其实在/lib目录下,在/lib/tls/i686/cmov/下也有。他们都是链接文件,刚开始我用cp命令拷贝他们,拷贝他们的链接文件也可以顺这链接找到原文件并拷贝过去,但在最后启动的时候老是提示/bin/sh: error while loading shared libraries: /libm.so.6: cannot open shared object file: No such file or directory
    我在网上找了很久也没找到答案,明明在在image/lib目录下有这个库,为什么老是提示说找不到呢,最后没办法,只好老老实实的把libm.so.6、 libc.so.6和/ld-linux.so.2这三个库所链接的文件ld-2.10.1.so、libc-2.10.1.so、libm-2.10.1.so拷贝到lib目录下,然后建立这三个文件的链接文件:
    ln -s ld-2.10.1.so  ld-linux.so.2 
    ln -s libc-2.10.1.so  libc.so.6 
    ln -s libm-2.10.1.so  libm.so.6
    这样后,最后启动成功,具体什么原因我还没去弄清楚,望知道的朋友告诉我,谢谢!
4.2在 image 下创建必要的目录和设备文件
    在image目录下运行
    mkdir proc etc sys mnt
    生成proc etc sys mnt这4个目录。
    hello world 已经创建了 console 设备文件,我们再用
    mknod -m 600 dev/null c 1 3
    命令创建另一个基本的设备文件。
4.3制作一个最简单的linux系统
    我们可以试着做一个最简单的可运行的 linux 系统了:
    首先删除image目录下的init文件,然后在image目录下写个init 的脚本,过程如下:
    (1)在 image 目录下写一个最简单的 init 脚本。
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
/bin/sh
    (2)为 init 脚本设置可执行权限,否则内核不会去执行它。
    chmod +x init
    (3)有些 busybox 配置中,mdev 命令需要读取/etc/mdev.conf 文件,为了避免出错信息,我们创建一个空文件。
    touch etc/mdev.conf
    (4)在内核源码目录下,执行
    make
    命令,重新编译内核,生成新的 initramfs。

    好了,在 QEMU 模拟环境下启动这个新的内核,系统初始化后,会进入 SHELL 环境。在这个 SHELL 环境下,试验一些常用命令,看看是否可以正常运行。我的运行如下图所示:



    上一步创建的简单 linux 系统在进入 SHELL 环境时,会打出下面这一句出错信息:
    /bin/sh: can't access tty; job controll off
    虽然不影响使用,但终究不够完美。产 生这个错误的原因是我们的 SHELL 是直接运行在内核的 console 上的,而 console 是不能提供控制终端(terminal)功能的,所以必须把 SHELL 运行在 tty 设备上,才能消除这个错误。解决问题的办法是使用正规 init 机制,在执行 SHELL 前打开 tty 设备。
另外,这个简单系统的 reboot、halt 等命令是不起作用的,也必须通过 init 方式解决。

   
    我们可以尝试在这个linux下运行tty这个命令,它打印出/dev/console,这说明它是运行在console上的。

4.4 busybox 的缺省 init 模式
    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
    我们试试这种模式是否可以解决我们的问题。

   
    说明一下,上面说的/etc/inittab是image/etc/inittab文件。并且是busybox工作。好了,我们开始下一个实验。将init脚本后面的/bin/sh那行删除,并在etc目录下建立init.d目录,再把init文件移到etc/init.d目录下更名为rcS
    mv init etc/init.d/rcS
         重新编译内核,生成新的 initramfs
    用 QEMU 试验一下新编译的内核。我的Qemu运行如下图:

    系统启动后,会打出一句话“please press Enter to active this console”——感觉还不错。但是按下回车键后,系统依然会打出错误信息“-/bin/sh:
can't access tty; job controll off ”。用 tty 命令看看当前的终端设备文件名:
# tty
/dev/console
    它还是 console,不是 tty 设备,所以问题没有解决。不过,reboot 和 halt 命令倒是可以正常工作了。
    经过验证,busybox 的缺省 init 模式无法满足我们的要求,我们还是要写 inittab,定制自己的 init 初始化流程。


4.5 busybox 的 inittab 文件格式说明
    要写自己的 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 的命令行。

4.6编写inittab
理解了 busybox 的 inittab 格式,我们就可以写 mini linux 的 inittab:
::sysinit:/etc/init.d/rcS
tty1::askfirst:/bin/sh
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh
tty5::askfirst:/bin/sh
tty6::askfirst:/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
    把这个文件放到 image 的 etc 目录下。为了执行 reboot 命令时避免提示找不到/etc/fstab 文件,我们再在 etc 目录下创建一个空文件
    touch fstab

   
    在image/etc目录下建立inittab文件,并将上面的内容写入这个文件。那么etc里的文件内容就如下了:fstab  init.d  inittab
    做好了这些,就可以重新编译内核,生成新的 initramfs 了。在 QEMU 试验环境下验证新生成的 linux,系统运行正常,而且象通常的 linux 系统一样,用 ALT+F1~F6 键可以在 6 个终端间切换。     注意:要在qemu里点击鼠标才可以用 ALT+F1~F6 键进行终端间切换。要释放鼠标,可以同时按住ctrl+alt并移动鼠标。


4.7配置内核支持initrd
    到目前为止,我们的 initramfs 都由内核编译系统生成的,并链接到内核中。其实我们也可以用 cpio 命令生成单独的 initramfs,与内核编译脱钩,在内核运行时以 initrd 的形式加载到内核,以增加灵活性。首 先配置内核使用单独的 initrd:在 Device Driver / Block device / 配置目录下,选择 RAM filesystem and RAMdisk ( initramfs/initrd ) support 配置项;再到 General Setup 配置目录项下,将 initramfs source file(s) 配置项原有的内容清空。然后把内核源码树的 usr 目录下已由内核编译生成的 initramfs 文件 initramfs_data.cpio.gz 拷贝到 ~/initramfs-test 目录下,我们先直接用这个文件试验一下 initrd 方式的 initramfs 的效果。最后,执行 make 命令重新编译内核后,在 QEMU 试验环境中,把 initrd 配置框(linux 配置框的下面)的内容 写为
~/initramfs-test/initramfs_data.cpio.gz,指定 initrd 的文件路径。

    好了,试验一下新的 initrd 方式的 initramfs 吧,效果跟先前的完全一样。
    以下是我做的步骤,因为我在根据《精通initramfs构建step by step》文后制作,由于里面说的不是很清楚,并且在Qemu Launch上的选项也没有,所以花了比较多的时间,并且我也把其中一些没有的步骤加上,多做实验,多了解一下。
    先做个实验。
    到general setup中,RAM filesystem and RAMdisk ( initramfs/initrd ) support选项下的的Initramfs source file(s)的内容清空。然后make,只好在启动Qemu Launch。
    然后你会看到什么,我这里的启动后的界面如下图:

    由于我们没有指定设备,所以启动到这里就没了,可以参考启动上面的信息。并且我们生成的bzImage文件也小了很多。我这里只有1.2M,原来的要2.2M。
    好,然后我们再做下一个实验。
     到general setup中,RAM filesystem and RAMdisk ( initramfs/initrd ) support选项下的的Initramfs source file(s)的内容填上原来的信息。然后make,在make的过程中我们可以看到
    GEN     usr/initramfs_data.cpio
    这样的信息。然后我们到linux-2.6.31/usr目录下把initramfs_data.cpio文件拷贝出来,我把它拷贝到了t1目录下。然后呢,然后就 到general setup中,RAM filesystem and RAMdisk ( initramfs/initrd ) support选项下的的Initramfs source file(s)的内容清空,然后make。这样生成一个没有指定设备的bzImage。根据上面的实验我们知道这个bzImage是不能完全启动的。然后我们在Qemu Launch的Linux boot的Init RAM disk里选择我们刚才拷贝出来的initramfs_data.cpio文件,注意,原来这里是我们自己用Qemu Launch生成的一个裸硬盘,现在不用这个裸硬盘,而是用initramfs_data.cpio文件。现在的Linux boot为这样的
    Kernel image:/home/fjb/home/test/initramfs/linux-2.6.31/arch/i386/boot/bzImage
    Initial RAM disk:/home/fjb/home/test/initramfs/t1/initramfs_data.cpio
    然后我们就可以启动Qemu Launch,现在的启动后又回到和我们可以用 ALT+F1~F6 键可以在 6 个终端间切换时的系统了。

4.8用cpio命令生成initramfs
    cpio 命令有三种操作模式:copy-out、copy-in、copy-pass,生成 initramfs 用的是它的
copy-out 模式,即把文件打包的操 作模式。cpio 的 copy-out 操作模式使用 -o 命令行选项指定。
缺省情况下,cpio 从标准输入读取输入数据,向标准输出写入输出数据。使用 -I 选项可以指定文件名
代替标准输入,使用 -O 选项可以指定文件名代替标准输出,而 -F 选项指定的文件名则根据 cpio 操作
模式的不同可代替标准输入或标准输出。
   
    把t1/image目录下的文件打包成 initramfs,进入t1/image目录,执行下面的命令:
    find . | cpio -o -H newc | gzip > ../image.cpio.gz
    命令执行完毕后,在~/t1 目录下就会生成文件名为 imgae.cpio.gz 的initramfs。
    上 面 cpio 命令的 -H 选项指定打包文件的具体格式,要生成 initramfs,只能用 newc 格式,如果使用其他格式,内核会打出这样的出错信息:Unpacking initramfs...<0> kernel panic - not syncing: no cpio magic 。

    在 QEMU 试验环境下试验一下把Initial RAM disk:选择改成
    /home/fjb/home/test/initramfs/t1/imgae.cpio.gz 。启动,效果跟先前的完全一样。
 
       cpio 命令的其他用法
    如 果我们要解开一个 cpio 格式的打包文件,则要使用 cpio 命令的 copy-in 操作模式。cpio 的 copy -out 操作模式使用 -i 命令行选项指定。例如,我们想把前一步从内核源码树 usr 目录下拷贝的initramfs_data.cpio 展开到~/t1/initramfs_data 目录下,则使用下列命令:
    mkdir ~/t1/initramfs_data
    cd ~/t1/initramfs_data
    cpio -i -F ../initramfs_data.cpio --no-absolute-filename
    命令执行完毕后,initramfs_data 目录下出现多个目录和文件,用 diff 命令比较 initramfs_data 与 image 目录,两者的完全一样。
    diff -yr --suppress-common-lines image initramfs_data
    我的输出是:
文件 image/dev/console 是 字符特殊文件,而 initramfs_data/dev/console 是 字符特殊文件
文件 image/dev/null 是 字符特殊文件,而 initramfs_data/dev/null 是 字符特殊文件
    所以这两个文件夹的内容是完全一样的。(说明一下diff的参数: -r 或--recursive  比较子目录中的文件。-y 或--side-by-side  以并列的方式显示文件的异同之处。--suppress-common-lines  在使用-y 参数时,仅显示不同之处。)


    上面 cpio 命令的 --no-absolute-filename 选项的作用是展开文件时,去掉文件路径最前面的"/", 把绝对路径名变为相对路径名。内核编译时生成的 initramfs 使用了绝对路径名,所以这个选项 必须使用,否则 initramfs 内文件展开到"/"目录去了,如果你是 root 用户或有"/"目录的写权限,那么展开的文件就有可能覆盖同名的文件(在 文件修改时间新于原有文件),那就糟糕了!
    展开文件前,你可能会想先看看打包文件里都有哪些文件,这时就要用 -t 选项了。例如,我们想看看内核编译时生成的 initramfs_data.cpio.gz 中都有哪些文件,我们就可以用下面的命令:
    zcat initramfs_data.cpio.gz | cpio -t
    在标准输出中打出文件名列表。
    使用 -v 选项可以在 cpio 命令执行时输出详细信息:在打包或展开文件时,输出已处理的文件名;与 - t 选项连用时,则显示文件的详细信息,类似 ls -l 的输出内容。-V 选项则用打点的方式,显示
cpio 命令的执行进度信息,一个点代表处理一个文件。


4.9 switch_root
    除了基于 initramfs 的系统(如第四节的 mini linux),通常 initramfs 都是为安装最终的根文件系统做准备工作,它的最后一步需要安装最终的根文件系统,然后切换到新根文件系统上去。以往 的基于 ramdisk 的 initrd 使用 pivot_root 命令切换到新的根文件系统,然后卸载 ramdisk。但是
initramfs 是 rootfs,而 rootfs 既不能 pivot_root,也不能 umount。为了从 initramfs 中切换
到新根文件系统,需要作如下处理:
    (1)删除 rootfs 的全部内容,释放空间
    find -xdev / -exec rm '{}' ';'
    (2)安装新的根文件系统,并切换
    cd /newmount; mount --move . /; chroot .
    (3)把 stdin/stdout/stderr 附加到新的/dev/console,然后执行新文件系统的 init 程序
上述步骤比较麻烦,而且要解决一个重要的问题:第一步删除 rootfs 的所有内容也删除了所有的命令,
那么后续如何再使用这些命令完成其他步骤?busybox 的解决方案是,提供了 switch_root 命令,完
成全部的处理过程,使用起来非常方便。
    switch_root 命令的格式是:
    switch_root [-c /dev/console] NEW_ROOT NEW_INIT [ARGUMENTS_TO_INIT]
    其中 NEW_ROOT 是实际的根文件系统的挂载目录,执行 switch_root 命令前需要挂载到系统中; NEW_INIT 是实际根文件系统的 init 程序的路径,一般是/sbin/init;-c /dev/console 是可选参
数,用于重定向实际的根文件系统的设备文件,一般情况我们不会使用;而 ARGUMENTS_TO_INIT 则是传递给实际的根文件系统的 init 程序的参数,也是可选的。
    需要特别注意的是:switch_root 命令必须由 PID=1 的进程调用,也就是必须由 initramfs 的 init 程序直接调用,不能由 init 派生的其他进程调用,否则会出错,提示:
    switch_root: not rootfs
    也是同样的原因,init 脚本调用 switch_root 命令必须用 exec 命令调用,否则也会出错,提示:
    switch_root: not rootfs



什么是mdev
    这是摘自hugerat的博客里的一些对mdev的说明(http://blog.csdn.net/hugerat/archive/2008/12/03/3437099.aspx):
    mdev是busybox自带的一个简化版的udev,适合于嵌入式的应用埸合。其具有使用简单的特点。它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程序所需的节点文件。在以busybox为基础构建嵌入式linux的根文件系统时,使用它是最优的选择。
    更详细的mdev的使用说明在mdev.txt文档中,你可以在busybox源代码树的docs目录下找到它。
    下面是摘自mdev.txt里的mdev的基本使用:
-----------
 Basic Use
-----------
Mdev has two primary uses: initial population and dynamic updates.  Both
require sysfs support in the kernel and have it mounted at /sys.  For dynamic
updates, you also need to have hotplugging enabled in your kernel.

Here's a typical code snippet from the init script:
[0] mount -t proc proc /proc
[1] mount -t sysfs sysfs /sys
[2] echo /bin/mdev > /proc/sys/kernel/hotplug
[3] mdev -s

Alternatively, without procfs the above becomes:
[1] mount -t sysfs sysfs /sys
[2] sysctl -w kernel.hotplug=/bin/mdev
[3] mdev -s


Of course, a more "full" setup would entail executing this before the previous
code snippet:
[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts

The simple explanation here is that [1] you need to have /sys mounted before
executing mdev.  Then you [2] instruct the kernel to execute /bin/mdev whenever
a device is added or removed so that the device node can be created or
destroyed.  Then you [3] seed /dev with all the device nodes that were created
while the system was booting.
For the "full" setup, you want to [4] make sure /dev is a tmpfs filesystem
(assuming you're running out of flash).  Then you want to [5] create the
/dev/pts mount point and finally [6] mount the devpts filesystem on it.
   
    我在这里就不翻译了,mdev的基本用法大概就是这样。

 

本文的pdf版下载:

跟我一步一步制作linux启动盘
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值