文件系统是对一个存储设备上的数据和元数据进行组织的机制,根文件系统是linux内核启动时所挂载的第一个文件系统。对于一个可启动的linux系统,根文件系统是其不可或缺的一部分。此处就根文件系统的构建作一个简单的介绍。

1. 根文件系统概述

linux系统为了精简以及便于维护,分成了内核空间以及用户空间。linux内核由内存管理、进程管理、设备驱动程序、网络管理等组成,它是操作系统的核心,具有很多最基本的功能,决定了系统的性能和稳定性。用户空间的文件系统用来提供管理系统的各种配置,提供相应的应用程序、服务、数据交换等。文件系统作为一种载体,它是用来实现用户与操作系统内核的交互。

linux内核启动完成之后,会尝试执行用户空间的第一个程序,init进程,用户空间根据这个进程,派生出其它的用户子进程。由于内核空间与用户空间的分离,linux内核必须要有一定的方法去加载执行用户空间的init进程,通常linux内核会在根文件系统相应的目录去尝试执行这个用户进程,如果未能加载执行,则内核panic。因此,一个可启动的linux系统必须包含linux内核以及一个根文件系统。

2. 构建最简单的根文件系统

根文件系统可以有不同的文件系统类型,如yaffs、网络文件系统NFS、initramfs等,也可以在不同的存储设备上,如nandflash、sd/mmc、u盘等。此处以initramfs为例说明根文件系统的构建。initramfs是一个ram文件系统,在initrd技术问题背景下,所提出一种更简单、更高效的新的处理方式。基于内存的文件系统,往往只是作为一种过渡手段,用来挂载系统真正的根文件系统,但对于很多嵌入式系统来说,initramfs往往也是最终的文件系统。

如前面所述,linux内核启动到最后会尝试加载执行根文件系统里的init用户程序,因此根文件系统里只要给内核提供init可执行文件,转到用户空间,linux系统则算启动成功。此处编写一个简单的init应用程序Hello world。

#include "stdio.h"

#include "stdlib.h"



int main(void)

{
int i;

i = 0;

while (1) {
printf("Helloworld %d\n", i);

sleep(1);

i++;

}

}

此处可用宿主机gcc进行编译执行以验证代码效果,然后用交叉编译工具链aarch64-linux-gnu-gcc静态编译,使之生成arm CPU可执行的指令,并且可脱离任何库独立运行。

在etc/profile中加入gcc-linaro-aarch64的环境变量。

export PATH=$PATH:/usr/lichee/brandy/toolchain/gcc-aarch64/bin

执行aarch64-linux-gnu-gcc -static -o init init.c,生成init可执行文件。

创建一个helloworld文件夹作为根文件系统目录,把init可执行文件放在helloworld目录下,用户空间还需要使用控制台,因此需要控制台设备文件。创建dev目录,linux内核在初始化rootfs时会自行创建console设备节点。因此,在helloworld目录下,一个最简单的打印Helloworld根文件系统只需init可执行文件、/dev目录。

在helloworld目录执行find . | cpio-o -H newc | gzip > ../rootfs.cpio.gz,在helloworld上一目录生成一个独立的rootfs.cpio.gz归档文件。rootfs被挂载后,linux内核会解压这个归档文件到rootfs,并尝试在rootfs中寻找/init,一旦找到init并执行,linux内核也就完成了启动工作。否则,linux内核回过头按照标准的做法解析参数“root=”,试图找到另一个文件系统并挂载。

进入到/lichee/linux-3.10目录,make menuconfig配置linux内核,确认已经支持initramfs。

03_构建根文件系统 - 图1

把rootfs.cpio.gz拷贝到lichee/linux-3.10目录中,编译linux内核。lichee/linux-3.10/scripts/build.sh内核编译脚本在编译完linux内核后,会尝试使用mkbootimg工具打包linux内核以及根文件系统档案rootfs.cpio.gz,并生成boot.img。bootloader可以通过加载boot.img并提取linux内核和ramdisk,实现内核的启动运行。

把编译生成的boot.img替换掉sd卡里的boot.img,上电启动。

03_构建根文件系统 - 图2

3. Busybox构建根文件系统

3.1. Busybox概述

对于linux系统来说,用户空间往往需要很多常用的linux命令、工具等来跟linux内核进行交互。这些命令集可以自行实现、也可以下载相应的命令源码进行移植,除此之外,还有一些开源的工具已经打包实现了linux常用的命令集,如busybox、embutils等。其中busybox是常用的一个工具,它能够以一个极小的应用程序来提供整个命令集的功能,而且需要哪些命令都是可以配置的,这非常适合于嵌入式系统的应用。

3.2. Busybox配置编译

busybox可以根据功能需求进行裁剪,键入make menuconfig对busybox进行配置,此处避免讨论使用动态库,用静态编译,使用交叉编译工具链aarch64-linux-gnu。

03_构建根文件系统 - 图3

make编译busybox,编译完后,make install安装,在源码目录_install有linuxrc链接文件以及命令集文件夹,/bin和/sbin,其中linuxrc链接到/bin/busybox,/bin和/sbin所有命令均是链接到/bin/busybox。

3.3. Busybox根文件系统

创建busybox目录作为根文件系统目录,把_install上的/bin、/sbin、以及linuxrc复制到busybox目录下,由于initramfs执行第一个用户程序为init,此处把linuxrc重命名为init即可。

busybox的一些命令需要一些目录、配置文件、设备等的支持。因此,基于busybox的根文件系统还需创建一些目录及文件。

此处以busybox源码目录examples/bootfloppy/etc为例说明/etc目录及其文件内容。busybox启动后,首先会解析/etc/inittab的内容,根据这个配置文件,进行相应的运行动作。

::sysinit:/etc/init.d/rcS

::respawn:-/bin/sh

::askfirst:-/bin/sh

::ctrlaltdel:/bin/umount -a -r

::restart:/sbin/init

sysinit表明系统启动后最先执行/etc/init.d/rcS启动脚本,最后启动/bin/sh子进程。/etc/init.d/rcS内容如下

#! /bin/sh

/bin/mount –a

脚本尝试挂载/etc/fstab列出的所有文件系统,因此需要/etc/fstab文件,其内容如下

proc     /proc    proc     defaults      0    0

sysfs    /sys      sysfs    defaults      0    0

可知尝试挂载proc文件系统到/proc目录,sysfs文件系统到/sys目录,因此对于这个busybox自带的/etc配置例程,根文件系统目录下需/proc、/sys目录,用mkdir即可创建目录。

在例程/etc目录下,还有一个profile文件,该文件用于配置每个用户登录系统后的环境变量。其内容如下

echo -n "Processing /etc/profile... "

PATH=/sbin:/bin

runlevel=S

prevlevel=N

umask 022

export PATH runlevel prevlevel

echo

busybox需要使用控制台等设备文件,因此还需要/dev目录以及该目录下简单的设备节点,linux内核在初始化rootfs时会自行创建console设备节点,因此可以不用手动创建。

至此基于busybox例程的配置修改完成,这是根据例程所实现较简单的目录及其配置,一般根据需要使用则需存在,不需使用则无需存在的原则,可以根据根文件系统的功能,进行更复杂的配置。

3.4. Busybox测试

在busybox目录下,执行find . |cpio -o -H newc | gzip > ../rootfs.cpio.gz,在busybox上一目录生成一个独立的rootfs.cpio.gz归档文件。

把rootfs.cpio.gz拷贝到lichee/linux-3.10目录中,重新编译linux内核,在/lichee/out/sun50iw2p1/linux/common目录生成新的boot.img镜像,把编译生成的boot.img替换掉sd卡里的boot.img,上电启动。

03_构建根文件系统 - 图4

测试之前的Hello world程序,./helloworld,用ctrl+c终止进程。

03_构建根文件系统 - 图5

用vi编辑生成文本文件,mkdir创建文件夹等,成功后,当前的文件系统有相应的操作改变,但系统重启后,基于ram文件系统的initramfs又回到之前的内容。

4. 附录

对于嵌入式系统来说,基于linux进行构建整个系统,往往是因为linux下驱动完善,有各种优秀的开源项目,网络等各种功能支持完备,从一定程度上减少了相当的开发工作。此时的linux系统往往尽量精简,只需达到相应的项目需求即可,采用initramfs来精简文件系统无疑是一种不错的方法,可以实现linux系统的秒启动。

本章所述的根文件系统
源码:http://pan.baidu.com/s/1cetsIE