尝试initramfs+ucLibc+busybox

来源:互联网 发布:windows 系统编程 编辑:程序博客网 时间:2024/05/12 12:24

尝试initramfs+ucLibc+busybox

作者: zjujoe 转载请注明出处

Emailzjujoe@yahoo.com

BLOGhttp://blog.csdn.net/zjujoe

 

前言

最近移植成功 onenandrfs 文件系统到 pxa310 平台, 测试了一下, 发现性能不错。 于是开始考虑将其应用到 meamo 文件系统。 供应商推荐的方案是根文件系统采用 camfs , 然后以 link方式将某些需要写权限的目录放到 rfs 文件系统里去。 但是应用组的大哥说为了软件扩展性,应该将根文件系统做成可写,即直接使用 rfs 作为根文件系统。

考虑到:

1.        rfs 文件系统不支持设备文件, 我们无法直接从内核 mount 一个 rfs 文件系统

2.        我们从供应商拿到的是没有源代码的 rfs 模块, 只能使用模块形式。

我们决定使用initramfs initramfs 里安装 rfs 模块,然后 switch_root rfs 文件系统。

 

了解 initramfs

阅读内核文档 Documentation/filesystems/ramfs-rootfs-initramfs.txt 获得关于 initramfs 的一些基础知识,得知应该采用 klibc 或者 ucLibc + busybox 作为 initramfs 的内容, 大体试了一下 klibc, 发现目前该项目还不够吃成熟(资料比较少, 邮件列表居然不能搜索,编译不能自动配置),决定采用 ucLibc + busybox 来组织文件系统。

 

编译 ucLibc

参考文档 http://blog.chinaunix.net/u1/43093/showart_338407.html编译 ucLibc, 具体过程很简单, 这里罗列一下自己的心得:

提示 1

一开始就请选择一个通用目录, 比如/opt/buildroot Buildroot 在编译过程中生成了一堆绝对路径,编译完了不能移动 buildroot(或者说移动很麻烦)。我第一次编译是在一个较深的目录里进行的, 编译完后移动 buildroot 目录, 编译 busybox 会出错。

提示 2

 

buildroot 会自动下载必须的源码到 dl 目录,包括:

-rw-r--r-- 1 zjujoe zjujoe 149622452007-08-29 05:42 binutils-2.18.tar.bz2

-rw-r--r-- 1 zjujoe zjujoe 397077202007-02-14 16:04 gcc-4.1.2.tar.bz2

-rw-r--r-- 1 zjujoe zjujoe  1747068 2007-09-11 23:01 gmp-4.2.2.tar.bz2

-rw-r--r-- 1 zjujoe zjujoe 494507292008-10-09 11:33 linux-2.6.26.6.tar.bz2

-rw-r--r-- 1 zjujoe zjujoe    22444 2007-10-23 11:34 mpfr-2.3.0.patch

-rw-r--r-- 1 zjujoe zjujoe   872947 2007-08-29 18:27 mpfr-2.3.0.tar.bz2

-rw-r--r-- 1 zjujoe zjujoe  2134864 2007-05-06 17:45 uClibc-0.9.29.tar.bz2

100M 左右, 如果你的网络不是很快,需要较长的时间,另外,编译 gcc 等也很花时间,所以最好在下班后执行这项活动。第二天早上过来一些已经OK

 

提示 3

memuconfig 时,某些配置会导致编译失败, 某些则编译成功后 再编译出来的busybox 不能正常使用, 本人第一次编译就遇到了一个编译出错:

make[4]:Entering directory`/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build/bfd/doc'

restore=:&& backupdir=".am$$" && /

        rm -rf $backupdir && mkdir$backupdir && /

        if(/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18/missing makeinfo--split-size=5000000 --split-size=5000000 --version) >/dev/null 2>&1;then /

          for f in bfd.info bfd.info-[0-9]bfd.info-[0-9][0-9] bfd.i[0-9] bfd.i[0-9][0-9]; do /

            if test -f $f; then mv $f$backupdir; restore=mv; else :; fi; /

          done; /

        else :; fi && /

        if/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18/missing makeinfo--split-size=5000000 --split-size=5000000  -I /home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18/bfd/doc /

         -o bfd.info `test -f 'bfd.texinfo' ||echo '/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18/bfd/doc/'`bfd.texinfo;/

        then /

          rc=0; /

        else /

          rc=$?; /

          $restore $backupdir/* `echo"./bfd.info" | sed 's|[^/]*$||'`; /

        fi; /

        rm -rf $backupdir; exit $rc

WARNING:`makeinfo' is missing on your system. You should only need it if

         you modified a `.texi' or `.texinfo'file, or any other file

         indirectly affecting the aspect of themanual.  The spurious

         call might also be the consequence ofusing a buggy `make' (AIX,

         DU, IRIX).  You might want to install the `Texinfo'package or

         the `GNU make' package.  Grab either from any GNU archive site.

make[4]:*** [bfd.info] Error 1

make[4]:Leaving directory `/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build/bfd/doc'

Makinginfo in po

make[4]:Entering directory`/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build/bfd/po'

make[4]:Nothing to be done for `info'.

make[4]:Leaving directory `/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build/bfd/po'

make[4]:Entering directory`/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build/bfd'

make[4]:Nothing to be done for `info-am'.

make[4]:Leaving directory`/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build/bfd'

make[3]:*** [info-recursive] Error 1

make[3]:Leaving directory`/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build/bfd'

make[2]:*** [all-bfd] Error 2

make[2]:Leaving directory`/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build'

make[1]:*** [all] Error 2

make[1]:Leaving directory`/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build'

make:***[/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18-build/binutils/objdump]Error 2

 

google 一下, 得到一个 workaround:

doc>pwd

/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18/bfd/doc

doc>makeinfo--split-size=5000000 --split-size=5000000 -I `pwd` -o bfd.info `test -f'bfd.texinfo' || echo '/home/zjujoe/buildroot/toolchain_build_arm/binutils-2.18/bfd/doc'`bfd.texinfo

 

另外, 用第一次编译编译出来的 ucLibc 编译出的 busybox, init 会失败:

process'-/bin/sh' (pid 16) exited. Scheduling it for restart.

跟踪一下, 发现 setjmp函数调用失败。(后来发现,buildroot 里有跟此相关的编译选项)

 

提示 4

第二次编译, 仍然采用 buildroot-20081019.tar.bz2 但是仔细的 check 了编译选项, 然后在下班时开始编译, 第二天早上过来,发现居然编译成功, 而且(后话)编译出来的 busybox 也没有任何问题。我的config 文件如下(删除了注释行):

BR2_HAVE_DOT_CONFIG=y

BR2_VERSION="0.10.0-svn"

BR2_arm=y

BR2_iwmmxt=y

BR2_ARM_TYPE="ARM_IWMMXT"

BR2_ARM_EABI=y

BR2_ARCH="arm"

BR2_ENDIAN="LITTLE"

BR2_GCC_TARGET_TUNE="iwmmxt"

BR2_GCC_TARGET_ARCH="iwmmxt"

BR2_GCC_TARGET_ABI="iwmmxt"

BR2_PROJECT="uclibc"

BR2_HOSTNAME="uclibc"

BR2_BANNER="Welcome tothe Erik's uClibc development environment."

BR2_BOARD_NAME="arm"

BR2_BOARD_PATH="target/device/ARM"

BR2_TARGET_ARM=y

BR2_PRIMARY_SITE=""

BR2_WGET="wget--passive-ftp -nd"

BR2_SVN_CO="svnco"

BR2_SVN_UP="svnup"

BR2_GIT="gitclone"

BR2_ZCAT="gzip -d-c"

BR2_BZCAT="bzcat"

BR2_TAR_OPTIONS=""

BR2_DL_DIR="$(BASE_DIR)/dl"

BR2_SOURCEFORGE_MIRROR="easynews"

BR2_KERNEL_MIRROR="http://www.kernel.org/pub/"

BR2_GNU_MIRROR="http://ftp.gnu.org/pub/gnu"

BR2_DEBIAN_MIRROR="http://ftp.debian.org"

BR2_ATMEL_MIRROR="ftp://www.at91.com/pub/buildroot/"

BR2_AT91_PATCH_MIRROR="http://maxim.org.za/AT91RM9200/2.6/"

BR2_STAGING_DIR="$(BUILD_DIR)/staging_dir"

BR2_TOPDIR_PREFIX=""

BR2_TOPDIR_SUFFIX=""

BR2_ROOTFS_PREFIX="rootfs"

BR2_ROOTFS_SUFFIX=""

BR2_GNU_BUILD_SUFFIX="pc-linux-gnu"

BR2_GNU_TARGET_SUFFIX="linux-uclibcgnueabi"

BR2_JLEVEL=1

BR2_RECENT=y

BR2_STRIP_strip=y

BR2_OPTIMIZE_2=y

BR2_UPDATE_CONFIG=y

BR2_TOOLCHAIN_BUILDROOT=y

BR2_TOOLCHAIN_SOURCE=y

BR2_EXT_GCC_VERSION_4_1_2=y

BR2_EXT_GCC_VERSION_4_2_1=y

BR2_EXT_GCC_VERSION_4_2_2=y

BR2_EXT_GCC_VERSION_4_2_3=y

BR2_EXT_BINUTILS_VERSION_2_17=y

BR2_EXT_UCLIBC_VERSION_0_9_29=y

BR2_EXT_UCLIBC_VERSION_0_9_28_3=y

BR2_KERNEL_HEADERS_2_6_26=y

BR2_DEFAULT_KERNEL_HEADERS="2.6.26.6"

BR2_UCLIBC_VERSION_0_9_29=y

BR2_UCLIBC_CONFIG="toolchain/uClibc/uClibc-0.9.29.config"

BR2_PTHREADS_OLD=y

BR2_BINUTILS_VERSION_2_18=y

BR2_BINUTILS_VERSION="2.18"

BR2_EXTRA_BINUTILS_CONFIG_OPTIONS=""

BR2_GCC_VERSION_4_1_2=y

BR2_GCC_VERSION="4.1.2"

BR2_GCC_USE_SJLJ_EXCEPTIONS=y

BR2_EXTRA_GCC_CONFIG_OPTIONS=""

BR2_GCC_SHARED_LIBGCC=y

BR2_SOFT_FLOAT=y

BR2_TARGET_OPTIMIZATION="-Os-pipe"

BR2_CROSS_TOOLCHAIN_TARGET_UTILS=y

BR2_KERNEL_none=y

提示 5

最终得到的文件系统及工具链位于:

文件系统: /opt/buildroot/project_build_arm/uclibc/root

工具链: /opt/buildroot/build_arm/staging_dir/usr/bin

 

将工具链列表路径加入PATH

 

编译 busybox

Buildroot 里自带了 busybox, 但是跟我们目前使用的版本相差较大,所以决定采用 busybox 1.6.1.

 

Busybox 是嵌入式文件系统里的瑞士军刀,这里就不多介绍了, 对我们而言,它是我们所需要的唯一的二进制文件。

 

修改 Makefile, 使其使用 uClibc 工具链:

CROSS_COMPILE   ?= arm-linux-uclibcgnueabi-

 

然后执行 Makemenuconfig, 注意选择:Ash MountInsmod Echo switch_root

配置文件如下:

CONFIG_HAVE_DOT_CONFIG=y

CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"

CONFIG_STATIC=y

CONFIG_NO_DEBUG_LIB=y

CONFIG_INSTALL_NO_USR=y

CONFIG_INSTALL_APPLET_SYMLINKS=y

CONFIG_PREFIX="./_install"

CONFIG_PASSWORD_MINLEN=6

CONFIG_MD5_SIZE_VS_SPEED=1

CONFIG_FEATURE_EDITING_HISTORY=

CONFIG_ECHO=y

CONFIG_MKNOD=y

CONFIG_TEST=y

CONFIG_INSMOD=y

CONFIG_FEATURE_2_6_MODULES=y

CONFIG_MOUNT=y

CONFIG_SWITCH_ROOT=y

CONFIG_FEATURE_LESS_MAXLINES=

CONFIG_FEATURE_SH_IS_ASH=y

CONFIG_ASH=y

CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=

 

执行 make install, 最终得到一个静态编译的 busybox (我们没有别的可执行二进制文件,也不需要执行外部程序)

 

制作一个 initramfs 文件系统

目录结构如下:

rootfs_initramfs>tree

.

|-- bin

|   |-- [ -> busybox

|   |-- [[ -> busybox

|   |-- ash -> busybox

|   |-- busybox

|   |-- echo -> busybox

|   |-- mknod -> busybox

|   |-- mount -> busybox

|   |-- sh -> busybox

|   `-- test -> busybox

|-- dev

|   |-- console

|   `-- stl4

|-- init

|-- lib

|   `-- modules

|       `-- 2.6.21

|           |-- rfs.ko

|           |-- xsr.ko

|           `-- xsr_stl.ko

|-- newroot

`-- sbin

    |-- insmod -> ../bin/busybox

`-- switch_root -> ../bin/busybox

 

其中 bin 以及 sbin busybox _install 子目录拷贝而来, dev 下的console 为启动必须的设备文件, stl4 则是我们未来的根分区:

crw------- 1 root root   5, 1 2008-01-01 08:04 console

brw-rw---- 1 root root 138, 4 2008-01-0108:04 stl4

 

lib/modules/2.6.21/下为 rfs 相关的内核模块:

-rw-r--r-- 1 root root  79073 2008-10-29 17:56 rfs.ko

-rw-r--r-- 1 root root 194280 2008-10-2917:56 xsr.ko

-rw-r--r-- 1 root root 128005 2008-10-2917:56 xsr_stl.ko

 

newroot 目录用于mount 未来的根文件系统。

 

/init 脚本为我们启动时执行代码,主要任务就是安装 rfs 模块并切换到新的根, 内容如下:

#! /bin/sh

insmod /lib/modules/2.6.21/xsr.ko   

insmod /lib/modules/2.6.21/xsr_stl.ko

insmod /lib/modules/2.6.21/rfs.ko

mount -t rfs /dev/stl4 /newroot -ocodepage=cp437

mount -t tmpfs none /newroot/dev

mknod /newroot/dev/console c 5 1

exec switch_root /newroot /sbin/init

 

编译内核

修改 CMDLINE:

CONFIG_CMDLINE="root=/dev/mtdblock4rootfstype=jffs2 rw console=ttyS2,115200 mem=128M lpj=3112960"

变为:

CONFIG_CMDLINE="console=ttyS2,115200mem=128Mlpj=3112960"

 

添加 initramfs 相关选项:

ONFIG_BLK_DEV_INITRD=y

CONFIG_INITRAMFS_SOURCE="rootfs_initramfs"

CONFIG_INITRAMFS_ROOT_UID=0

CONFIG_INITRAMFS_ROOT_GID=0

 

其中CONFIG_INITRAMFS_SOURCE 指向我们前面制作的文件系统。

其它的改动按照自己的环境来。

 

Make 完内核后我们就得到一个包含一个小文件系统(大概 300K)的内核, 它会在启动后去 mount 位于 /dev/stl4 rfs 根分区。

 

结语

利用 Initramfs +ucLibc + busybox 我们得到一个微型的文件系统(100k-500k),可以应用到各种嵌入式场景. 比如升级系统, 关机充电系统,等等。或者作为启动部分(如我们上面这个情景)。

利用 ucLibc +busybox, 我们可以得到一个较小的文件系统 (500k – 5M), 加上嵌入式 GUI, 比如 MiniGUI 我们可以做一些简单的 GUI 应用。

再大些的系统由于本身比较复杂, 可能会用到 ucLibc 不支持的特性, 就不能也不该使用 ucLibc 了。比如 Qtopia/Meamo