作者: zjujoe 转载请注明出处
Email:[email protected]
BLOG:http://blog.csdn.net/zjujoe
最近移植成功 onenand rfs 文件系统到 pxa310 平台, 测试了一下, 发现性能不错。 于是开始考虑将其应用到 meamo 文件系统。 供应商推荐的方案是根文件系统采用 camfs , 然后以 link方式将某些需要写权限的目录放到 rfs 文件系统里去。 但是应用组的大哥说为了软件扩展性, 应该将根文件系统做成可写,即直接使用 rfs 作为根文件系统。
考虑到:
1. rfs 文件系统不支持设备文件, 我们无法直接从内核 mount 一个 rfs 文件系统
2. 我们从供应商拿到的是没有源代码的 rfs 模块, 只能使用模块形式。
我们决定使用 initramfs , 在 initramfs 里安装 rfs 模块,然后 switch_root 到 rfs 文件系统。
阅读内核文档 Documentation/filesystems/ramfs-rootfs-initramfs.txt, 获得关于 initramfs 的一些基础知识,得知应该采用 klibc 或者 ucLibc + busybox 作为 initramfs 的内容, 大体试了一下 klibc, 发现目前该项目还不够吃成熟(资料比较少, 邮件列表居然不能搜索,编译不能自动配置), 决定采用 ucLibc + busybox 来组织文件系统。
参考文档 http://blog.chinaunix.net/u1/43093/showart_338407.html 编译 ucLibc, 具体过程很简单, 这里罗列一下自己的心得:
一开始就请选择一个通用目录, 比如/opt/buildroot ,Buildroot 在编译过程中生成了一堆绝对路径,编译完了不能移动 buildroot(或者说移动很麻烦)。我第一次编译是在一个较深的目录里进行的, 编译完后移动 buildroot 目录, 编译 busybox 会出错。
buildroot 会自动下载必须的源码到 dl 目录,包括:
-rw-r--r-- 1 zjujoe zjujoe 14962245 2007-08-29 05:42 binutils-2.18.tar.bz2
-rw-r--r-- 1 zjujoe zjujoe 39707720 2007-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 49450729 2008-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。
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 the manual. The spurious
call might also be the consequence of using 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'
Making info 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 里有跟此相关的编译选项)
第二次编译, 仍然采用 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 to the 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="svn co"
BR2_SVN_UP="svn up"
BR2_GIT="git clone"
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
最终得到的文件系统及工具链位于:
文件系统: /opt/buildroot/project_build_arm/uclibc/root
工具链: /opt/buildroot/build_arm/staging_dir/usr/bin
将工具链列表路径加入 PATH。
Buildroot 里自带了 busybox, 但是跟我们目前使用的版本相差较大,所以决定采用 busybox 1.6.1 .
Busybox 是嵌入式文件系统里的瑞士军刀, 这里就不多介绍了, 对我们而言,它是我们所需要的唯一的二进制文件。
修改 Makefile, 使其使用 uClibc 工具链:
CROSS_COMPILE ?= arm-linux-uclibcgnueabi-
然后执行 Make menuconfig, 注意选择:Ash Mount Insmod 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。 (我们没有别的可执行二进制文件,也不需要执行外部程序)
目录结构如下:
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-01 08: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-29 17:56 xsr.ko
-rw-r--r-- 1 root root 128005 2008-10-29 17: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 -o codepage=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/mtdblock4 rootfstype=jffs2 rw console=ttyS2,115200 mem= 128M lpj=3112960"
变为:
CONFIG_CMDLINE="console=ttyS2,115200 mem= 128M lpj=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。