根文件系统制作(一)

在前面的内核移植中,使用的是开发板上原有的根文件系统,这里就来讨论一下如何自己制作根文件系统。
根文件系统可以由JFFS2、YAFFS2、UBIFS等文件系统支持,这里仍就选择UBIFS文件系统来制作根文件系统。UBIFS是Unsorted Block Image File System(无序区块镜像文件系统)的缩写,是一种较新的文件系统。UBIFS作为JFFS2的后继文件系统之一,在设计与性能上均较YAFFS2、JFFS2更适合于MLC NAND FLASH存储器。Linux内核从2.6.27以后的版本都提供了对它的支持,因此实现起来比较方便。
根文件系统实际上就是Linux根目录树的一个集合,由一些特定的目录、设备节点文件、内核模块文件、库文件、命令集以及其它一些配置文件组成。这个集合最终依赖于某个类型的文件系统来实现,下面就具体来讨论基于UBIFS文件系统的根文件系统制作过程。
首先要建立一个存放根文件集合的目录,这里就在根目录下建立一个名为rootfs的目录,把整个根文件系统都放在这个目录下。接下来进入rootfs目录,并在该目录下建立根目录树的第一层目录,执行“mkdir bin sbin etc dev proc lib sys var mnt usr tmp media home”产生出这些目录。
然后进入dev目录,在该目录下需要制作两个默认的设备节点文件,分别执行“mknod console c 5 1”和“mknod null c 1 3”命令来生在console和null两个设备节点文件。其中,mknod为制作设备节点命令,后面跟的是设备节点文件的名称,c是指该设备为字符型设备,后面跟的是主设备号和次设备号。
接下来需要拷贝glibc库文件到lib目录下,以支持某些应用程序的运行。glibc库文件位于交叉编译工具链的lib目录下,进入交叉编译工具链所在的目录(本例为4.4.3/arm-none-linux-gnueabi/),然后执行命令“cp ./lib/*.so* /rootfs/lib -d”就可以把交叉编译工具链下的库文件拷贝到rootfs/lib目录下来,参数“-d”的意思是在拷贝时保持软链接文件属性不变。这里的交叉编译工具链目录最好是用于编译Linux内核时所使用的工具链目录,也即根文件系统的库文件要与所使用的Linux内核配套(本教程中交叉编译工具都使用4.4.3版本)。如果在交叉编译工具链下没有这些库文件,则需要通过源码重新制作交叉编译工具链,以生成这些库文件。
库文件拷贝完成后,还可以把Linux的内核模块文件也拷贝过来。内核模块文件就是在通过make menuiconfig配置Linux内核时勾选为“M”的那些模块文件,由于前面我们只执行make zImage编译了内核映像文件,所以还没有生成这些内核模块文件,此处执行“make modules”命令就可对内核模块进行编译并生成模块文件。内核模块文件产生后,执行“make modules_install ARCH=arm INSTALL_MOD_PATH=/rootfs”命令进行安装,安装其实就是拷贝,完成后,在rootfs/lib目录下会多出一个名为modules的目录,里面就是内核模块文件。内核模块文件一都较大、较多,如果不是必须的就不要拷贝了,避免浪费Flash空间。
接下来要制作命令集,一般Linux命令的可执行程序都位于bin目录和sbin目录下,但由于平台不一样,所以此处并不能简单的把虚拟机bin目录和sbin目录下的文件拷贝过来使用。在嵌入式系统中使用的命令集,一般是由第三方软件Busybox来产生的。它由busybox一个可执行文件来提供所需命令,命令其实是由对busybox的软链接文件来充当的,下面就来制作busybox。
Busybox的源码可从官网(https://busybox.net)上下载,此处以1.20.2版本为例,下载后得到一个文件busybox-1.20.2.tar.bz2,执行命令“tar -jxvf busybox-1.20.2.tar.bz2”将其解压,完成后进入解压目录,就可看到busybox的源码文件了。同内核源码一样,要交叉编译需要修改Makefile文件,在顶层Makefile文件中找到“CROSS_COMPILE ?=” 一句,把其补充完整为“CROSS_COMPILE ?= arm-linux-”,再找到“ARCH ?= $(SUBARCH)”一句,把它改成“ARCH ?= arm”,完成后存盘退出。接下来执行命令“make defconfig”使用默认参考配置,完毕后再执行“make menuconfig”进行配置修改,如下图所示。

根文件系统制作(一)

在上图中可见,其菜单形式与内核配置完全一样。在配置菜单中,命令是以功能来分类的,比如与编辑相关的命令(如vi)都在Editors选项下,与网络相关的命令(如ifconfig)都在Networking Utilities选项下,如此等等。需要的命令进行勾选,不需要的不勾选。在生成busybox可执行程序时,有动态库和静态库两种方式可选。若要使用静态库方式,需要勾选Busybox Settings->Build Options->Build Busybox as a static binary(no shared libs)一项,若要使用动态库方式,需要勾选Busybox Settings->Build Options->Build shared libbusybox一项。动态库在运行时需要库文件的支持,但文件小;静态库可独立运行,但文件大。如果在根文件系统的lib目录下提供了库文件(libcrypt.so.1、libm.so.6、libc.so.6及ld-linux.so.2)的支持,busybox可编译成动态库形式,否则须编译成静态库形式。当然,如果为了确保命令集能够顺利运行,也可直接编译成静态库形式,文件也比动态库形式大不了多少。还有一点要注意,若想在嵌入式Linux中也实现提示符下显示路径的功能,在配置Busybox时还要勾选Busybox Settings->Busybox Library Tuning下的“Username completion”和“Fancy shell prompts”两项,这两项默认是不勾选的。还有,若前面嫌改Makefile文件麻烦,也可在Busybox Settings->Build Options->Cross Compiler prefix一项中把前缀设置为“arm-linux-”就可以了。
配置完成后执行make命令进行编译,编译完成后执行“make CONFIG_PREFIX=/rootfs install”命令进行安装,安装完成后,在rootfs目录下就会多出一个名为linuxrc的链接文件,并在bin目录和sbin目录中出现了命令文件。仔细观察会发现,除了bin目录下的busybox是一个可执行文件,其余的命令文件都是指向busybox的一些符号链接文件,这也是Busybox的独到之处。
编译Busybox时要注意一下交叉编译工具版本的选择,不同的busybox及不同的交叉编译工具版本都会直接影响到是否能编译成功,本例使用busybox为1.20.2版本,交叉编译工具为4.4.3版本,经测试能够成功编译。若要更换其他版本请自行尝试。
命令集安装完成后,接下来还需要对根文件系统进行配置。首先执行命令“vi /rootfs/etc/inittab”,在rootfs的etc目录下新建一个名为inittab的文件,并在其中录入以下四行内容:
::sysinit:/etc/init.d/rcS
console::respawn:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
录完后保存退出。inittab文件是Linux启动后的第一个进程init执行时所依赖的文件,init进程根据inittab文件的内容来创建其他进程,比如挂接文件系统、启动shell等。inittab内容的格式是固定的,其中定义的登记项都是以冒号隔开的四个段,即:“id:runlevels:action:process”的形式。其中,id段是每个登记项的标识符,用于唯一标识每个登记项,不能重复;runlevels段表示系统的运行级别,分为0~6共七个级别;action段表示对应登记项的process在一定条件下所要执行的动作,该项的动作较多,如sysinit表示系统启动,respawn表示当process终止后会马上重新启动,askfirst会在控制台上显示“Please press Enter to active this console”并在重新启动进程之前等待用户按下回车键,ctrlaltdel表示键盘同时按下组合键ctrl+alt+del的动作,shutdown表示关机的动作等等;process段表示具体启动哪个程序、脚本,或执行哪个命令,如果本段以“-”符号开头,则表示后面的程序是“交互”式的。id段如果为空,表示使用与init进程一样的控制台。runlevels段如果为空,表示所有的级别都执行。如果在根文件系统中没有inittab文件,则Busybox的init进程将会使用其默认的条目进行启动。
在上面的inittab文件中,sysinit项会去执行一个名为/etc/init.d/rcS的脚本文件,因此还要去创建这个文件。先执行命令“mkdir /rootfs/etc/init.d”创建目录,然后执行“vi /rootfs/etc/init.d/rcS”创建rcS脚本文件,并在其中录入以下内容:
#!/bin/sh
mount –a
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev –s
ifconfig eth0 192.168.1.90
完成后还需要给rcS脚本文件加上可执行属性,执行“chmod 755 rcS”即可。rcS文件定义了系统启动后所需要执行的一系列命令,主要任务是挂载文件系统及自动创建设备节点文件。在rcS文件中,第一行“#!/bin/sh”是指下面的脚本用/bin/sh来执行。第二行必须是“mount -a”,即首先要进行文件系统的挂载,所挂载的具体文件系统由/etc目录下的文件fstab来确定。所以还需要创建该文件,执行“vi /etc/fstab”创建fstab文件,并在其中录入以下内容:
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
录入时要注意,上述文件中每列(共6列)应该是对齐的(即每列之间的空格应以tab键来充当)。在fstab文件中,第1列表示要挂载的设备名称,第2列表示挂载点,第3列表示文件系统类型,第4列表示挂载的参数,第5列表示一个用来备份文件的程序,第6列表示一个用来检查磁盘的程序。
在上述fstab文件中,第一行表示挂载proc文件系统,挂载的目录(挂载点)为“/proc”。proc文件系统是一个系统自动生成和管理的虚拟文件系统,它没有实际的设备,里面的目录、文件都是由内核临时生成的,用来表示系统的运行状态。
第二行表示挂载tmpfs文件系统,挂载的目录(挂载点)为“/tmp”。tmpfs文件系统是一个临时文件系统,挂载后一些需要生成临时文件的程序就可都放到“/tmp”目录下,这样可减少系统对Flash存储器的操作,延长使用寿命。
第三行表示挂载sysfs文件系统,挂载的目录(挂载点)为“/sys”。sysfs文件系统是一个系统级的文件系统,它是udev机制所依赖的文件系统。udev是用户程序,它能够根据系统中硬件设备状态动态地更新设备文件,并在“/dev”目录下自动地创建设备节点。在Busybox中是使用mdev来实现udev功能的,mdev可认为是udev的一个简化版。
第四行表示根文件系统下的“/dev”目录也挂载tmpfs文件系统。由于使用mdev机制来自动生成“/dev”目录下的设备节点文件,为了减少对每次启动时对Flash存储器的操作,也把“/dev”目录挂载成为临时文件系统。
有了文件以上这些文件系统的支持,就可以使用mdev机制来动态的创建设备节点文件了,这是由前面rcS文件中的最后一行“mdev -s”来实现的,实现后就可以在“/dev”目录下看到自动建立的设备节点文件了。 有一点要说明一下,在“/dev”目录下原来手动创建的console和null两个设备节点文件不变,其余的文件才是由mdev机制动态建立的。由于init进程在执行时需要依赖console和null两个文件,所以在mdev实现之前就得先创建出这两个文件,所以它们不能通过mdev来创建。
在rcS文件中,第三行“mkdir /dev/pts”是创建devpts文件系统的挂载点(注:由于前面执行了mount -a,所以此时的dev目录是由临时文件系统担任的)。第四行“mount -t devpts devpts /dev/pts”是挂载devpts文件系统。pts是远程虚拟终端,devpts即远程虚拟终端文件设备,它是目前最常见的pseudo终端(PTYs)实现方式,通过“/dev/pts”目录可以了解目前远程虚拟终端的基本情况。此处挂载devpts主要是让系统支持远程外部网络连接的虚拟终端,如telnet、ssh等。第五行“echo /sbin/mdev > /proc/sys/kernel/hotplug”是设置内核,它将用户空间的应用程序路径传递给内核,之后kernel会在每次设备出现变动时调用已传递到用户空间的应用程序/sbin/mdev来处理对应的信息,进而mdev应用程序操作/dev目录下的设备,进行添加或删除。第六行“mdev -s”是启用mdev机制。最后一行“ifconfig eth0 192.168.1.90”是在启动系统设定系统的IP地址。
通过创建以上三个文件(inittab、inittab、fstab),就完成了对根文件系统的基本配置,然而有时候为方便使用,我们还会对其进行一定的功能扩充,比如一般会让开发板具备外存储器(SD卡及U盘等)的自动挂载功能。要实现这个功能,除了在rcS文件中加入“echo /sbin/mdev > /proc/sys/kernel/hotplug”一句以外,还需额外建立两个文件。执行“vi /etc/mdev.conf”新建一个mdev的配置文件,在其中输入以下内容:
mmcblk[0-9]p[1-9] 0:0 666 */etc/mdev.sh
sd[a-x][1-9] 0:0 666 */etc/mdev.sh
录入时同样注意,上述文件中每列(共4列)应该是对齐的(即每列之间的空格应以tab键来充当)。第一行表示SD卡(MMC卡)的设备节点,第二行表示U盘的设备节点。系统中的hotplug就是依据mdev.conf文件来生成设备节点的。
mdev.conf的格式为:<device regex> <uid>:<gid> <octal permissions> [<@|$|*> <command>]
其中,<device regex>为设备名称,支持正则表达式,例如上面的mmcblk[0-9]p[1-9];<uid>:<gid>分别为用户ID和用户所在组ID,默认为0:0;<octal permissions>为设备属性,即权限,数字表示与普通文件的一样,666表示可读可写;[<@|$|*> <command>]这个选项是当设备匹配成功时所执行的指令(command),指令可以是自己编写的脚本,指令前面有三个符号,@表示在设备节点创建完执行,$表示在设备节点删除前执行,*表示在设备节点创建完和删除前执行,该符号只能选其中的一个。
接下来执行“vi /etc/mdev.sh”创建在mdev.conf中调用的执行脚本文件,并在其中输入以下内容:
#!/bin/sh
if [ "$ACTION" = "remove" ] ; then
echo -e "umount /media/$MDEV\n" > /dev/ttySAC1
/bin/umount /media/$MDEV
/bin/rm -rf /media/$MDEV
else
echo -e "mount /dev/$MDEV /media/$MDEV\n" > /dev/ttySAC1
/bin/mkdir -p /media/$MDEV
/bin/mount /dev/$MDEV /media/$MDEV
fi
脚本中的变量ACTION表征了拔插的动作,如果是移除则进行自动反挂载并在console端打印出提示信息,如果是插入则进行自动设备挂载并在console端打印出提示信息。最后还要给mdev.sh脚本文件加上可执行属性,执行“chmod 755 mdev.sh”即可。这样配置后,在插入外存储器时,就会自动将其挂载到“/media”下,挂载点以设备节点名称命名,移除时会自动进行反挂载。
至此,根文件系统的全部配置就完成了,接下来就可以进行根文件系统移植了。

相关推荐