63. 根文件系统构建
一、根文件系统简介
根文件系统也叫做rootfs。大家学习STM32的W25Q256、SD卡的时候接触过FATFS文件系统。Rootfs?=FATFS。不等于!
FATFS这类的文件系统属于Linux内核的一部分,属于软件代码。
那么ROOTFS是什么,根文件系统=“根”、“文件”、“系统”。根文件系统就是一堆的文件、比如软件、配置文件等,这些文件是Linux运行所必须的,将他们组合在一起就构成了跟文件系统。
根文件系统就是一个“文件夹”,此“文件夹”有很多个文件,这些文件是linux运行所必须的,但是无法放到内核里面去的。比如命令、库、配置文件等等。所有这些软件需要我们自己构建。
/就是Linux根文件系统。
二、BusyBox构建根文件系统
https://busybox.net/
Download Source
构建根文件系统,busybox。还有很多成熟化的根文件系统构建方式,buildroot,yocto。构建的根文件系统调试我们通过nfs网络挂载,也就是跟文件系统存放到ubuntu下,开发板启动以后通过nfs服务使用ubuntu下的跟文件系统。
1、修改makefile,添加交叉编译器
修改ARCH和CROSS_COMPILE。
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
2、busybox支持中文字符
如果默认直接编译 busybox 的话,在使用 SecureCRT 的时候中文字符是显示不正常的,中文字符会显示为“?”,比如你的中文目录,中文文件都显示为“?”。不知道从哪个版本开始 busybox中的 shell 命令对中文输入即显示做了限制,即使内核支持中文但在 shell 下也依然无法正确显示。
所以我们需要修改 busybox 源码,取消 busybox 对中文显示的限制,打开文件 busybox- 1.29.0/libbb/printable_string.c,找到函数 printable_string,缩减后的函数内容如下
/* vi: set sw=4 ts=4: */
/*
* Unicode support routines.
*
* Copyright (C) 2010 Denys Vlasenko
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
#include "libbb.h"
#include "unicode.h"
const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
{
char *dst;
const char *s;
s = str;
while (1) {
unsigned char c = *s;
if (c == '\0') {
/* 99+% of inputs do not need conversion */
if (stats) {
stats->byte_count = (s - str);
stats->unicode_count = (s - str);
stats->unicode_width = (s - str);
}
return str;
}
if (c < ' ')
break;
if (c >= 0x7f)
break;
s++;
}
#if ENABLE_UNICODE_SUPPORT
dst = unicode_conv_to_printable(stats, str);
#else
{
char *d = dst = xstrdup(str);
while (1) {
unsigned char c = *d;
if (c == '\0')
break;
if (c < ' ' || c >= 0x7f)
*d = '?';
d++;
}
if (stats) {
stats->byte_count = (d - dst);
stats->unicode_count = (d - dst);
stats->unicode_width = (d - dst);
}
}
#endif
return auto_string(dst);
}
改为
const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
{
char *dst;
const char *s;
s = str;
while (1) {
unsigned char c = *s;
if (c == '\0') {
/* 99+% of inputs do not need conversion */
if (stats) {
stats->byte_count = (s - str);
stats->unicode_count = (s - str);
stats->unicode_width = (s - str);
}
return str;
}
if (c < ' ')
break;
// if (c >= 0x7f)
// break;
s++;
}
#if ENABLE_UNICODE_SUPPORT
dst = unicode_conv_to_printable(stats, str);
#else
{
char *d = dst = xstrdup(str);
while (1) {
unsigned char c = *d;
if (c == '\0')
break;
// if (c < ' ' || c >= 0x7f)
if (c < ' ')
*d = '?';
d++;
}
if (stats) {
stats->byte_count = (d - dst);
stats->unicode_count = (d - dst);
stats->unicode_width = (d - dst);
}
}
#endif
return auto_string(dst);
}
接着打开文件 busybox-1.29.0/libbb/unicode.c,找到如下内容:
static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
{
char *dst;
unsigned dst_len;
unsigned uni_count;
unsigned uni_width;
if (unicode_status != UNICODE_ON) {
char *d;
if (flags & UNI_FLAG_PAD) {
d = dst = xmalloc(width + 1);
while ((int)--width >= 0) {
unsigned char c = *src;
if (c == '\0') {
do
*d++ = ' ';
while ((int)--width >= 0);
break;
}
*d++ = (c >= ' ' && c < 0x7f) ? c : '?';
src++;
}
*d = '\0';
} else {
d = dst = xstrndup(src, width);
while (*d) {
unsigned char c = *d;
if (c < ' ' || c >= 0x7f)
*d = '?';
d++;
}
}
if (stats) {
stats->byte_count = (d - dst);
stats->unicode_count = (d - dst);
stats->unicode_width = (d - dst);
}
return dst;
}
dst = NULL;
uni_count = uni_width = 0;
dst_len = 0;
while (1) {
int w;
wchar_t wc;
#if ENABLE_UNICODE_USING_LOCALE
{
mbstate_t mbst = { 0 };
ssize_t rc = mbsrtowcs(&wc, &src, 1, &mbst);
/* If invalid sequence is seen: -1 is returned,
* src points to the invalid sequence, errno = EILSEQ.
* Else number of wchars (excluding terminating L'\0')
* written to dest is returned.
* If len (here: 1) non-L'\0' wchars stored at dest,
* src points to the next char to be converted.
* If string is completely converted: src = NULL.
*/
if (rc == 0) /* end-of-string */
break;
if (rc < 0) { /* error */
src++;
goto subst;
}
if (!iswprint(wc))
goto subst;
}
#else
src = mbstowc_internal(&wc, src);
/* src is advanced to next mb char
* wc == ERROR_WCHAR: invalid sequence is seen
* else: wc is set
*/
if (wc == ERROR_WCHAR) /* error */
goto subst;
if (wc == 0) /* end-of-string */
break;
#endif
if (CONFIG_LAST_SUPPORTED_WCHAR && wc > CONFIG_LAST_SUPPORTED_WCHAR)
goto subst;
w = wcwidth(wc);
if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0) /* non-printable wchar */
|| (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
|| (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
) {
subst:
wc = CONFIG_SUBST_WCHAR;
w = 1;
}
width -= w;
/* Note: if width == 0, we still may add more chars,
* they may be zero-width or combining ones */
if ((int)width < 0) {
/* can't add this wc, string would become longer than width */
width += w;
break;
}
uni_count++;
uni_width += w;
dst = xrealloc(dst, dst_len + MB_CUR_MAX);
#if ENABLE_UNICODE_USING_LOCALE
{
mbstate_t mbst = { 0 };
dst_len += wcrtomb(&dst[dst_len], wc, &mbst);
}
#else
dst_len += wcrtomb_internal(&dst[dst_len], wc);
#endif
}
/* Pad to remaining width */
if (flags & UNI_FLAG_PAD) {
dst = xrealloc(dst, dst_len + width + 1);
uni_count += width;
uni_width += width;
while ((int)--width >= 0) {
dst[dst_len++] = ' ';
}
}
dst[dst_len] = '\0';
if (stats) {
stats->byte_count = dst_len;
stats->unicode_count = uni_count;
stats->unicode_width = uni_width;
}
return dst;
}
改为
static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
{
char *dst;
unsigned dst_len;
unsigned uni_count;
unsigned uni_width;
if (unicode_status != UNICODE_ON) {
char *d;
if (flags & UNI_FLAG_PAD) {
d = dst = xmalloc(width + 1);
while ((int)--width >= 0) {
unsigned char c = *src;
if (c == '\0') {
do
*d++ = ' ';
while ((int)--width >= 0);
break;
}
/* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
*d++ = (c >= ' ') ? c : '?';
src++;
}
*d = '\0';
} else {
d = dst = xstrndup(src, width);
while (*d) {
unsigned char c = *d;
/* if (c < ' ' || c >= 0x7f) */
if(c < ' ')
*d = '?';
d++;
}
}
if (stats) {
stats->byte_count = (d - dst);
stats->unicode_count = (d - dst);
stats->unicode_width = (d - dst);
}
return dst;
}
dst = NULL;
uni_count = uni_width = 0;
dst_len = 0;
while (1) {
int w;
wchar_t wc;
#if ENABLE_UNICODE_USING_LOCALE
{
mbstate_t mbst = { 0 };
ssize_t rc = mbsrtowcs(&wc, &src, 1, &mbst);
/* If invalid sequence is seen: -1 is returned,
* src points to the invalid sequence, errno = EILSEQ.
* Else number of wchars (excluding terminating L'\0')
* written to dest is returned.
* If len (here: 1) non-L'\0' wchars stored at dest,
* src points to the next char to be converted.
* If string is completely converted: src = NULL.
*/
if (rc == 0) /* end-of-string */
break;
if (rc < 0) { /* error */
src++;
goto subst;
}
if (!iswprint(wc))
goto subst;
}
#else
src = mbstowc_internal(&wc, src);
/* src is advanced to next mb char
* wc == ERROR_WCHAR: invalid sequence is seen
* else: wc is set
*/
if (wc == ERROR_WCHAR) /* error */
goto subst;
if (wc == 0) /* end-of-string */
break;
#endif
if (CONFIG_LAST_SUPPORTED_WCHAR && wc > CONFIG_LAST_SUPPORTED_WCHAR)
goto subst;
w = wcwidth(wc);
if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0) /* non-printable wchar */
|| (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
|| (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
) {
subst:
wc = CONFIG_SUBST_WCHAR;
w = 1;
}
width -= w;
/* Note: if width == 0, we still may add more chars,
* they may be zero-width or combining ones */
if ((int)width < 0) {
/* can't add this wc, string would become longer than width */
width += w;
break;
}
uni_count++;
uni_width += w;
dst = xrealloc(dst, dst_len + MB_CUR_MAX);
#if ENABLE_UNICODE_USING_LOCALE
{
mbstate_t mbst = { 0 };
dst_len += wcrtomb(&dst[dst_len], wc, &mbst);
}
#else
dst_len += wcrtomb_internal(&dst[dst_len], wc);
#endif
}
/* Pad to remaining width */
if (flags & UNI_FLAG_PAD) {
dst = xrealloc(dst, dst_len + width + 1);
uni_count += width;
uni_width += width;
while ((int)--width >= 0) {
dst[dst_len++] = ' ';
}
}
dst[dst_len] = '\0';
if (stats) {
stats->byte_count = dst_len;
stats->unicode_count = uni_count;
stats->unicode_width = uni_width;
}
return dst;
}
3、配置busybox
我们一般使用默认配置即可,因此使用如下命令先使用默认配置来配置一下 busybox:
make defconfig
ls -a
有.config 说明配置成功
busybox 也支持图形化配置,通过图形化配置我们可以进一步选择自己想要的功能,输入如下命令打开图形化配置界面: 打开图形化配置界面
make menuconfig
make menuconfig报错
busybox/busybox-1.29.0# make menuconfig
HOSTCC scripts/kconfig/lxdialog/checklist.o
: fatal error: curses.h: No such file or directory
安装:
sudo apt-get install libncurses5-dev libncursesw5-dev
配置路径如下:
Location:
-> Settings
-> Build static binary (no shared libs
选项“Build static binary (no shared libs)”用来决定是静态编译 busybox 还是动态编译,静态编译的话就不需要库文件,但是编译出来的库会很大。动态编译的话要求根文件系统中有库文件,但是编译出来的 busybox 会小很多。这里我们不能采用静态编译!因为采用静态编译的话 DNS 会出问题!无法进行域名解析,配置如图 38.2.2.
继续配置如下路径配置项:
Location: -> Settings
-> vi-style line editing commands
继续配置如下路径配置项: Location:
-> Linux Module Utilities -> Simplified modutils
默认会选中“Simplified modutils”,这里我们要取消勾选!!结果如图 38.2.2.5 所示
继续配置如下路径配置项: Location:
-> Linux System Utilities
-> mdev (16 kb) //确保下面的全部选中,默认都是选中的
最后就是使能 busybox 的 unicode 编码以支持中文,配置路径如下: Location:
-> Settings
-> Support Unicode -> Check $LC_ALL, $LC_CTYPE and $LANG environment variables //选中//选中
4、编译busybox
配置好 busybox 以后就可以编译了,我们可以指定编译结果的存放目录,我们肯定要将编译结果存放到前面创建的 rootfs 目录中,输入如下命令:
make -j12 install CONFIG_PREFIX=/home/mk/nfs/rootfs
编译完成以后会在 busybox 的所有工具和文件就会被安装到 rootfs 目录中, rootfs 目录内容如图 38.2.2.9 所示:
从图 38.2.2.9 可以看出, rootfs 目录下有 bin、 sbin 和 usr 这三个目录,以及 linuxrc 这个文件。前面说过 Linux 内核 init 进程最后会查找用户空间的 init 程序,找到以后就会运行这个用户空间的 init 程序,从而切换到用户态。如果 bootargs 设置 init=/linuxrc,那么 linuxrc 就是可以作为用户空间的 init 程序,所以用户态空间的 init 程序是 busybox 来生成的。
busybox 的工作就完成了,但是此时的根文件系统还不能使用,还需要一些其他的文件,我们继续来完善 rootfs。
5、拷贝库文件。
库文件就是交叉编译器的库文件。
1. 先是rootfs/lib目录
在 rootfs 中创建一个名为“lib”的文件夹 ,进入如下路径对应的目录:
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linuxgnueabihf/libc/lib
此目录下有很多的so(是通配符)和.a 文件,这些就是库文件,将此目录下所有的so*和.a文件都拷贝到 rootfs/lib 目录中, 拷贝命令如下:
cp so *.a /home/mike/nfs/rootfs/lib/ -d
后面的“-d”表示拷贝符号链接,这里有个比较特殊的库文件: ld-linux-armhf.so.3,此库文件也是个符号链接,相当于 Windows 下的快捷方式。会链接到库 ld-2.19-2014.08-1-git.so 上,输
入命令“ls ld-linux-armhf.so.3 -l”查看此文件详细信息,如图 38.2.3.1 所示
ld-linux-armhf.so.3 后面有个“->”,表示其是个软连接文件,链接到文件 ld-2.19-2014.08-1-git.so,因为其是一个“快捷方式”,因此大小只有 24B。但是, ld-linuxarmhf.so.3 不能作为符号链接,否则的话在根文件系统中执行程序无法执行!所以我们需要 ldlinux-armhf.so.3 完成逆袭,由“快捷方式”变为“本尊”,方法很简单,那就是重新复制 ld-linuxarmhf.so.3,只是不复制软链接即可,先将 rootfs/lib 中的 ld-linux-armhf.so.3 文件删除掉,命令如下:
rm ld-linux-armhf.so.3
然 后 重 新 进 入 到 /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/armlinux-gnueabihf/libc/lib 目录中,重新拷贝 ld-linux-armhf.so.3,命令如下:
cp ld-linux-armhf.so.3 /home/mike/nfs/rootfs/lib/
拷贝完成以后再到 rootfs/lib 目录下查看 ld-linux-armhf.so.3 文件详细信息,如图 38.2.3.2 所示
-rwxr-xr-x 1 xxx xxx 724392 Feb 13 21:28 ld-linux-armhf.so.3
此时 ld-linux-armhf.so.3 已经不是软连接了,而是实实在在的一个库文件,而且文件大小为 724392B。
继续进入如下目录中:
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib
此目录下也有很多的的so和.a 库文件,我们将其也拷贝到 rootfs/lib 目录中,命令如下:
cp *so* *.a /home/mike/nfs/rootfs/lib/ -d
rootfs/lib 目录的库文件就这些了,完成以后的 rootfs/lib 目录如图 38.2.3.3 所示
2. 然后是rootfs/usr/lib目录
在 rootfs 的 usr 目录下创建一个名为 lib 的目录,将如下目录中的库文件拷贝到 rootfs/usr/lib目录下:
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
将此目录下的 so 和.a 库文件都拷贝到 rootfs/usr/lib 目录中,命令如下:
cp *so* *.a /home/mike/nfs/rootfs/usr/lib/ -d
完成以后的 rootfs/usr/lib 目录如图 38.2.3.4 所示
至此,根文件系统的库文件就全部添加好了,可以使用“du”命令来查看一下 rootfs/lib 和rootfs/usr/lib 这两个目录的大小,命令如下:
cd rootfs
//进入根文件系统目录
du ./lib ./usr/lib/ -sh
//查看 lib 和 usr/lib 这两个目录的大小
结果如图 38.2.3.5 所示:
6、创建其他文件夹
mkdir dev proc mnt sys tmp root
目前来看,这个根文件系统好像已经准备好了,究竟有没有准备好,直接测一下就知道了!
三、根文件系统初步测试
为了方便测试,我们采用挂在网络根文件系统,nfs。要求:
1、linux内核网络驱动要工作正常。
2、设置uboot的bootargs,也就是linux内核的命令行参数。

从ubuntu的tftpboot里面加载我们前面移植的uboot、zImage和dtb,设置bootcmd。
bootcmd=tftp 80800000 zImage;tftp 83000000 imx6ull-my-emmc.dtb;bootz 80800000 - 83000000;
修改
bootargs
bootargs=console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw;
为
setenv bootargs 'console=ttymxc0,115200 rw root=/dev/nfs nfsroot=192.168.31.213:/home/mk/nfs/rootfs ip=192.168.31.50:192.168.31.213:192.168.31.1:255.255.255.0::eth0:off'
setenv serverip 192.168.31.213
saveenv
测试的时候发现:
也就是提示 文件系统为只读,这是因为booargs参数没设置全。
还有问题
can't run '/etc/init.d/rcS': No such file or directory
四、完善根文件系统
五、根文件系统其他功能测试
1、测试应用程序的运行
编写helloc.c,测试软件试运行在ARM开发板上,因此编译的时候要使用交叉编译器。编译完成以后使用file命令查看可执行文件信息。
应用程序后台运行 xxx &。关闭某个软件后台运行,输入ps命令查看当前所有软件的PID。查到hello的PID是77。通过kill -9 77(PID).
2、中文字符测试
3、开机自启动