嵌入式Linux--根文件系统(二)BusyBox构建根文件系统

简介

 BusyBox 最初是由 Bruce Perens 在 1996 年为 Debian GNU/Linux 安装盘编写的。其目的是在软盘上创建一个可引导的 GNU/Linux 系统,做安装和救急盘。BusyBox 是一个集成了三百多个最常用 Linux 命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat 和 echo 命令等等,还包含了一些更大、更复杂的工具,例 grep、find、mount 以及 telnet 命令。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。简单的说 BusyBox 就好像是个大工具箱,它集成压缩了 Linux的许多工具和命令,也包含了 Android 系统自带的 shell。

官网地址为:https://busybox.net/

在这里插入图片描述
 然后点击官网左侧“Get BusyBox”栏中的“Download Source”进入下载页面。如下图所示:

在这里插入图片描述
 这是一个开源的程序,并且一直在更新中,我们使用的版本是“busybox-1.29.0.tar.bz2”。

 我们也可以在光盘目录“i.MX6UL 终结者光盘资料\07_文件系统源码\1.busybox 源码”中的busybox-1.29.0.tar.bz2。

一、制作最小文件系统

 一般我们在 Linux 驱动开发的时候都是通过 nfs 挂载根文件系统的,当产品最终上市开卖的时候才会将根文件系统烧写到 EMMC 或者 NAND 中。所以要在 4.2.1 小节中设置的 nfs 服务器目录中创建一个名为 rootfs 的子目录(名字大家可以随意起,为了方便就用了 rootfs),比如我的电脑中“/home/zuozhongkai/linux/nfs”就是我设置的 NFS 服务器目录,使用如下命令创建名
为 rootfs 的子目录:

mkdir rootfs

创建好的 rootfs 子目录就用来存放我们的根文件系统了。
将 busybox-1.29.0.tar.bz2 发送到 Ubuntu 中,存放位置大家随便选择。然后使用如下命令将其解压:

tar -vxjf busybox-1.29.0.tar.bz2

解压完成以后进入到 busybox-1.29.0 目录中,此目录中的文件和文件夹如图 38.2.2.1 所示:

liefyuan@ubuntu:~/linux/busybox/busybox-1.29.0$ ls
applets        editors      Makefile                qemu_multiarch_testing
applets_sh     examples     Makefile.custom         README
arch           findutils    Makefile.flags          runit
archival       include      Makefile.help           scripts
AUTHORS        init         make_single_applets.sh  selinux
Config.in      INSTALL      miscutils               shell
configs        klibc-utils  modutils                size_single_applets.sh
console-tools  libbb        networking              sysklogd
coreutils      libpwdgrp    NOFORK_NOEXEC.lst       testsuite
debianutils    LICENSE      NOFORK_NOEXEC.sh        TODO
docs           loginutils   printutils              TODO_unicode
e2fsprogs      mailutils    procps                  util-linux

1、 修改Makefile,添加编译器

 同 Uboot 和 Linux 移植一样,打开 busybox 的顶层 Makefile,添加 ARCH 和 CROSS_COMPILE的值,如下所示:

164 CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-4.9.4-2017.01-
x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
......
190 ARCH ?= arm

在代码中 CORSS_COMPILE 使用了绝对路径!主要是为了防止编译出错。

2、支持中文字符

 如果默认直接编译 busybox 的话,在使用 SecureCRT 的时候中文字符是显示不正常的,中文字符会显示为“?”,比如你的中文目录,中文文件都显示为“?”。不知道从哪个版本开始 busybox中的 shell 命令对中文输入即显示做了限制,即使内核支持中文但在 shell 下也依然无法正确显示。

 所以我们需要修改 busybox 源码,取消 busybox 对中文显示的限制,打开文件 busybox-1.29.0/libbb/printable_string.c,找到函数 printable_string,缩减后的函数内容如下:

  • 31行的:if (c >= 0x7f) break; 全部注释掉
  • 45行的:if (c < ' ' || c >= 0x7f) 修改为 if(c < ' ')
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++;
...

 以上部分的代码就是被修改以后的,主要就是禁止字符大于 0X7F 以
后 break 和输出‘?’。

 接着打开文件 busybox-1.29.0/libbb/unicode.c,找到如下内容:

  • 1022行的:*d++ = (c >= ' ' && c < 0x7f) ? c : '?';修改为*d++ = (c >= ' ') ? c : '?';
  • 1030行的:if (c < ' ' || c >= 0x7f) 修改为 if(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 : '?';*/
                *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++;
            }
        }

 部分的代码就是被修改以后的,同样主要是禁止字符大于 0X7F 的时候设置为‘?’。busybox 中文字符支持跟代码修改有关的就改好了,最后还需要配置 busybox来使能 unicode 码,这个稍后我们配置 busybox 的时候在设置。

3、配置busybox

 跟编译 Uboot、Linux kernel 一样,我们要先对 busybox 进行默认的配置,有以下几种配置选项:

  • defconfig:缺省配置,也就是默认配置选项。
  • allyesconfig:全选配置,也就是选中 busybox 的所有功能。
  • allnoconfig,最小配置。

 我们一般使用默认配置即可,因此使用如下命令先使用默认配置来配置一下 busybox:

make defconfig

 busybox 也支持图形化配置,通过图形化配置我们可以进一步选择自己想要的功能,输入如下命令打开图形化配置界面:

make menuconfig

 打开以后如下图所示:
在这里插入图片描述

1、不采用静态编译

配置路径如下:

Location:
	-> Settings
		-> Build static binary (no shared libs)

 选项“Build static binary (no shared libs)”用来决定是静态编译 busybox 还是动态编译busybox,静态编译的话就不需要库文件,但是编译出来的库会很大。动态编译的话要求根文件系统中有库文件,但是编译出来的 busybox 会小很多。这里我们不能采用静态编译!因为采用静态编译的话 DNS 会出问题!无法进行域名解析,配置如下图所示:

在这里插入图片描述
在哪上图中,不选择“Build static binary (no shared libs)”

2、配置vi-style

继续配置如下路径配置项:

Location:
	-> Settings
		-> vi-style line editing commands

选择“vi-style line editing commands”
在这里插入图片描述

3、取消勾选Simplified modutils

Location:
	-> Linux Module Utilities
		-> Simplified modutils

默认会选中“Simplified modutils”,这里我们要取消勾选!!结果如图:
在这里插入图片描述

4、配置mdev

继续配置如下路径配置项:

Location:
	-> Linux System Utilities
		-> mdev (16 kb) //确保下面的全部选中,默认都是选中的

在这里插入图片描述

5、配置支持中文

最后就是使能 busybox 的 unicode 编码以支持中文,配置路径如下:

Location:
	-> Settings
		-> Support Unicode //选中
			-> Check $LC_ALL, $LC_CTYPE and $LANG environment variables  //选中

在这里插入图片描述
 busybox 的配置就到此结束了,大家也可以根据自己的实际需求选择配置其他的选项,不过对于初学者笔者不建议再做其他的修改,可能会出现编译出错的情况发生。

4、编译busybox

配置好 busybox 以后就可以编译了,我们可以指定编译结果的存放目录,我们肯定要将编译结果存放到前面创建的 rootfs 目录中,输入如下命令:

make
make install CONFIG_PREFIX=/home/liefyuan/linux/nfs/rootfs

 COFIG_PREFIX 指 定 编 译 结 果 的 存 放 目 录 , 比 如 我 存 放 到“/home/liefyuan/linux/nfs/rootfs”目录中,等待编译完成。编译完成以后如图所示:

liefyuan@ubuntu:~/linux/busybox/busybox-1.29.0$ make install CONFIG_PREFIX=/home/liefyuan/linux/nfs/rootfs
  /home/liefyuan/linux/nfs/rootfs//bin/arch -> busybox
 ...
  /home/liefyuan/linux/nfs/rootfs//usr/sbin/ubidetach -> ../../bin/busybox
  /home/liefyuan/linux/nfs/rootfs//usr/sbin/ubimkvol -> ../../bin/busybox
  /home/liefyuan/linux/nfs/rootfs//usr/sbin/ubirename -> ../../bin/busybox
  /home/liefyuan/linux/nfs/rootfs//usr/sbin/ubirmvol -> ../../bin/busybox
  /home/liefyuan/linux/nfs/rootfs//usr/sbin/ubirsvol -> ../../bin/busybox
  /home/liefyuan/linux/nfs/rootfs//usr/sbin/ubiupdatevol -> ../../bin/busybox
  /home/liefyuan/linux/nfs/rootfs//usr/sbin/udhcpd -> ../../bin/busybox


--------------------------------------------------
You will probably need to make your busybox binary
setuid root to ensure all configured applets will
work properly.
--------------------------------------------------

 编译完成以后会在 busybox 的所有工具和文件就会被安装到 rootfs 目录中,rootfs 目录内容,如图所示:

liefyuan@ubuntu:~/linux/nfs/rootfs$ ls
bin  linuxrc  sbin  usr

从上面可以看出,rootfs 目录下有 bin、sbin 和 usr 这三个目录,以及 linuxrc 这个文件。前面说过 Linux 内核 init 进程最后会查找用户空间的 init 程序,找到以后就会运行这个用户空间的 init 程序,从而切换到用户态。如果 bootargs 设置 init=/linuxrc,那么 linuxrc 就是可以作为用户空间的 init 程序,所以用户态空间的 init 程序是 busybox 来生成的

 busybox 的工作就完成了,但是此时的根文件系统还不能使用,还需要一些其他的文件,我们继续来完善 rootfs。

二、向根文件系统添加 lib 库

1、向rootfs的“/lib”目录添加库文件

 Linux 中的应用程序一般都是需要动态库的,当然你也可以编译成静态的,但是静态的可执行文件会很大。如果编译为动态的话就需要动态库,所以我们需要先根文件系统中添加动态库。在 rootfs 中创建一个名为“lib”的文件夹,命令如下:

mkdir lib

 lib 文件夹创建好了,库文件从哪里来呢?lib 库文件从交叉编译器中获取,前面我们搭建交叉编译环境的时候将交叉编译器存放到了“/usr/local/arm/”目录中。交叉编译器里面有很多的库文件,这些库文件具体是做什么的我们作为初学者肯定不知道,既然我不知道那就简单粗暴的把所有的库文件都放到我们的根文件系统中。这样做出来的根文件系统肯定很大,但是我们现在是学习阶段,还做不了裁剪。这就是为什么我们推荐大家购买512MB+8GB版本的EMMC核心版,如果后面要学习 QT 的话那占用的空间将更大,不裁剪的话 512MB 的 NAND 完全不够用的!而裁剪又是需要经验的,我们都是初学者,哪里来的经验啊。所以我们推荐初学者购
买 EMMC 版核心板并不是说为了多赚大家的钱,而是从实际角度考虑的。
进入如下路径对应的目录:

/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib

 此目录下有很多的so(是通配符)和.a 文件,这些就是库文件,将此目录下所有的so*和.a文件都拷贝到 rootfs/lib 目录中,拷贝命令如下:

cp *so* *.a /home/liefyuan/linux/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”查看此文件详细信息,如下所示:

liefyuan@ubuntu:~/linux/nfs/rootfs/lib$ ls ld-linux-armhf.so.3 -l
lrwxrwxrwx 1 11827 9000 24 Feb  1  2017 ld-linux-armhf.so.3 -> ld-2.19-2014.08-1-git.so

 从上面可以看出,ld-linux-armhf.so.3 后面有个“->”,表示其是个软连接文件,链接到文件 ld-2.19-2014.08-1-git.so,因为其是一个“快捷方式”,因此大小只有 24B。但是,ld-linux-armhf.so.3 不能作为符号链接,否则的话在根文件系统中执行程序无法执行!所以我们需要 ld-linux-armhf.so.3 完成逆袭,由“快捷方式”变为“本尊”,方法很简单,那就是重新复制 ld-linux-armhf.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/arm-linux-gnueabihf/libc/lib 目录中,重新拷贝 ld-linux-armhf.so.3,命令如下:

cp ld-linux-armhf.so.3 /home/liefyuan/linux/nfs/rootfs/lib/

 拷贝完成以后再到 rootfs/lib 目录下查看 ld-linux-armhf.so.3 文件详细信息,如下面所示:

liefyuan@ubuntu:~/linux/nfs/rootfs/lib$ ls ld-linux-armhf.so.3 -l
-rwxr-xr-x 1 liefyuan liefyuan 724392 Dec 13 15:54 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/liefyuan/linux/nfs/rootfs/lib/ -d

rootfs/lib 目录的库文件就这些了,完成以后的 rootfs/lib 目录如下所示:

liefyuan@ubuntu:~/linux/nfs/rootfs/lib$ ls
ld-2.19-2014.08-1-git.so               libnss_compat.so.2
ld-linux-armhf.so.3                    libnss_db-2.19-2014.08-1-git.so
libanl-2.19-2014.08-1-git.so           libnss_db.so.2
libanl.so.1                            libnss_dns-2.19-2014.08-1-git.so
libasan.a                              libnss_dns.so.2
libasan.so                             libnss_files-2.19-2014.08-1-git.so
libasan.so.1                           libnss_files.so.2
libasan.so.1.0.0                       libnss_hesiod-2.19-2014.08-1-git.so
libatomic.a                            libnss_hesiod.so.2
libatomic.so                           libnss_nis-2.19-2014.08-1-git.so
libatomic.so.1                         libnss_nisplus-2.19-2014.08-1-git.so
libatomic.so.1.1.0                     libnss_nisplus.so.2
libBrokenLocale-2.19-2014.08-1-git.so  libnss_nis.so.2
libBrokenLocale.so.1                   libpcprofile.so
libc-2.19-2014.08-1-git.so             libpthread-2.19-2014.08-1-git.so
libcrypt-2.19-2014.08-1-git.so         libpthread.so.0
libcrypt.so.1                          libresolv-2.19-2014.08-1-git.so
libc.so.6                              libresolv.so.2
libdl-2.19-2014.08-1-git.so            librt-2.19-2014.08-1-git.so
libdl.so.2                             librt.so.1
libgcc_s.so                            libSegFault.so
libgcc_s.so.1                          libssp.a
libgfortran.a                          libssp_nonshared.a
libgfortran.so                         libssp.so
libgfortran.so.3                       libssp.so.0
libgfortran.so.3.0.0                   libssp.so.0.0.0
libgomp.a                              libstdc++.a
libgomp.so                             libstdc++.so
libgomp.so.1                           libstdc++.so.6
libgomp.so.1.0.0                       libstdc++.so.6.0.20
libitm.a                               libstdc++.so.6.0.20-gdb.py
libitm.so                              libsupc++.a
libitm.so.1                            libthread_db-1.0.so
libitm.so.1.0.0                        libthread_db.so.1
libm-2.19-2014.08-1-git.so             libubsan.a
libmemusage.so                         libubsan.so
libm.so.6                              libubsan.so.0
libnsl-2.19-2014.08-1-git.so           libubsan.so.0.0.0
libnsl.so.1                            libutil-2.19-2014.08-1-git.so
libnss_compat-2.19-2014.08-1-git.so    libutil.so.1

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/liefyuan/linux/nfs/rootfs/usr/lib/ -d

完成以后的 rootfs/usr/lib 目录如下所示:

liefyuan@ubuntu:~/linux/nfs/rootfs/usr/lib$ ls
libanl.a               libdl.a              libnss_db_pic.a         libresolv_p.a
libanl_p.a             libdl_p.a            libnss_db.so            libresolv_pic.a
libanl_pic.a           libdl_pic.a          libnss_dns_pic.a        libresolv_pic.map
libanl.so              libdl.so             libnss_dns.so           libresolv.so
libBrokenLocale.a      libg.a               libnss_files_pic.a      librpcsvc.a
libBrokenLocale_p.a    libieee.a            libnss_files.so         librpcsvc_p.a
libBrokenLocale_pic.a  libm.a               libnss_hesiod_pic.a     librt.a
libBrokenLocale.so     libmcheck.a          libnss_hesiod.so        librt_p.a
libc.a                 libm_p.a             libnss_nis_pic.a        librt_pic.a
libc_nonshared.a       libm_pic.a           libnss_nisplus_pic.a    librt.so
libc_p.a               libm.so              libnss_nisplus.so       libthread_db_pic.a
libc_pic.a             libnsl.a             libnss_nis.so           libthread_db.so
libcrypt.a             libnsl_p.a           libpthread.a            libutil.a
libcrypt_p.a           libnsl_pic.a         libpthread_nonshared.a  libutil_p.a
libcrypt_pic.a         libnsl.so            libpthread_p.a          libutil_pic.a
libcrypt.so            libnss_compat_pic.a  libpthread.so           libutil.so
libc.so                libnss_compat.so     libresolv.a

 至此,根文件系统的库文件就全部添加好了,可以使用“du”命令来查看一下 rootfs/lib 和rootfs/usr/lib 这两个目录的大小,命令如下:

cd rootfs //进入根文件系统目录
du ./lib ./usr/lib/ -sh //查看 lib 和 usr/lib 这两个目录的大小

结果如下所示:

liefyuan@ubuntu:~/linux/nfs/rootfs$ du ./lib ./usr/lib -sh
57M	./lib
67M	./usr/lib

 可以看出lib和usr/lib这两个文件的大小分别为57MB和67MB,加起来就是57+67=124MB。非常大!所以正点原子的 256MB 和 512MB 的 NAND 核心版就不是给初学者准备的,而是给大批量采购的企业准备的,还是那句话,初学者选择 EMMC 版本的。

三、创建其他文件夹

 在根文件系统中创建其他文件夹,如 dev、proc、mnt、sys、tmp 和 root 等,创建完成以后,如下面所示:

liefyuan@ubuntu:~/linux/nfs/rootfs$ mkdir dev
liefyuan@ubuntu:~/linux/nfs/rootfs$ mkdir proc
liefyuan@ubuntu:~/linux/nfs/rootfs$ mkdir mnt
liefyuan@ubuntu:~/linux/nfs/rootfs$ mkdir sys
liefyuan@ubuntu:~/linux/nfs/rootfs$ mkdir tmp
liefyuan@ubuntu:~/linux/nfs/rootfs$ mkdir root
liefyuan@ubuntu:~/linux/nfs/rootfs$ ls
bin  dev  lib  linuxrc  mnt  proc  root  sbin  sys  tmp  usr

目前来看,这个根文件系统好像已经准备好了,究竟有没有准备好,直接测一下就知道了!

四、根文件系统初步测试

 接下来我们使用测试一下前面创建好的根文件系统 rootfs,测试方法就是使用 NFS 挂载,uboot 里面的 bootargs 环境变量会设置“root”的值,所以我们将 root 的值改为 NFS 挂载即可。在 Linux 内核源码里面有相应的文档讲解如何设置,文档为 Documentation/filesystems/nfs/nfsroot.txt,格式如下:

root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
  • <server-ip>:服务器 IP 地址,也就是存放根文件系统主机的 IP 地址,那就是 Ubuntu 的 IP地址,比如我的 Ubuntu 主机 IP 地址为 192.168.1.250。
  • <root-dir> :根文件系统的存放路径,比如我的就是/home/zuozhongkai/linux/nfs/rootfs。
  • <nfs-options>:NFS 的其他可选选项,一般不设置。
  • <client-ip> :客户端 IP 地址,也就是我们开发板的 IP 地址,Linux 内核启动以后就会使用此 IP 地址来配置开发板。此地址一定要和 Ubuntu 主机在同一个网段内,并且没有被其他的设备使用,在 Ubuntu 中使用 ping 命令 ping 一下就知道要设置的 IP 地址有没有被使用,如果不能ping 通就说明没有被使用,那么就可以设置为开发板的 IP 地址,比如我就可以设置为192.168.1.251。
  • <server-ip> :服务器 IP 地址,前面已经说了。
  • <gw-ip> :网关地址,我的就是 192.168.1.1。
  • <netmask>:子网掩码,我的就是 255.255.255.0。
  • <hostname>:客户机的名字,一般不设置,此值可以空着。
  • <device> :设备名,也就是网卡名,一般是 eth0,eth1….,正点原子的 I.MX6U-ALPHA 开发板的 ENET2 为 eth0,ENET1 为 eth1。如果你的电脑只有一个网卡,那么基本只能是 eth0。这里我们使用 ENET2,所以网卡名就是 eth0。
  • <autoconf> :自动配置,一般不使用,所以设置为 off。
  • <dns0-ip> :DNS0 服务器 IP 地址,不使用。
  • <dns1-ip> :DNS1 服务器 IP 地址,不使用。

设置网络信息

setenv ipaddr 192.168.0.121
setenv ethaddr de:b4:7c:de:dc:07
setenv gatewayip 192.168.0.1
setenv netmask 255.255.255.0
setenv serverip 192.168.0.120
saveenv

根据上面的格式 bootargs 环境变量的 root 值如下:

root=/dev/nfs nfsroot=192.168.0.120:/home/liefyuan/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.251:192.168.1.250:192.168.1.1:255.255.255.0::eth0:off

 “proto=tcp”表示使用 TCP 协议,“rw”表示 nfs 挂载的根文件系统为可读可写。启动开发板,进入 uboot 命令行模式,然后重新设置 bootargs 环境变量,命令如下:

setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.0.120:/home/liefyuan/linux/nfs/rootfs,proto=tcp rw ip=192.168.0.121:192.168.1.120:192.168.0.1:255.255.255.0::eth0:off' //设置 bootargs
saveenv //保存环境变量

设置好以后使用“boot”命令启动 Linux 内核,结果如下所示:

=> setenv ipaddr 192.168.0.121
=> setenv ethaddr de:b4:7c:de:dc:07
=> setenv gatewayip 192.168.0.1
=> setenv netmask 255.255.255.0
=> setenv serverip 192.168.0.120
=> saveenv
Saving Environment to MMC...
Writing to MMC(1)... done
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.0.120:/home/liefyuan/linux/nfs/rootfs,proto=tcp rw ip=192.168.0.121:192.168.1.120:192.168.0.1:255.255.255.0::eth0:off'
=> saveenv
Saving Environment to MMC...
Writing to MMC(1)... done
=> boot
reading zImage
6788696 bytes read in 223 ms (29 MiB/s)
reading imx6ull-14x14-emmc-4.3-800x480-c.dtb
38823 bytes read in 18 ms (2.1 MiB/s)
Kernel image @ 0x80800000 [ 0x000000 - 0x679658 ]
## Flattened Device Tree blob at 83000000
   Booting using the fdt blob at 0x83000000
   Using Device Tree in place at 83000000, end 8300c7a6

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.1.15 (liefyuan@ubuntu) (gcc version 4.9.4 (Linaro GCC 4.9-2017.01) ) #1 SMP PREEMPT Sat Dec 12 13:45:03 CST 2020
[    0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c53c7d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] Machine model: Freescale i.MX6 ULL 14x14 EVK Board
[    0.000000] Reserved memory: created CMA memory pool at 0x98000000, size 128 MiB
[    0.000000] Reserved memory: initialized node linux,cma, compatible id shared-dma-pool
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] PERCPU: Embedded 12 pages/cpu @97b90000 s16832 r8192 d24128 u49152
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 130048
[    0.000000] Kernel command line: console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.0.120:/home/liefyuan/linux/nfs/rootfs,proto=tcp rw ip=192.168.0.121:192.168.1.120:192.168.0.1:255.255.255.0::eth0:off
[    0.000000] PID hash table entries: 2048 (order: 1, 8192 bytes)
[    0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
[    0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
[    0.000000] Memory: 375280K/524288K available (8531K kernel code, 454K rwdata, 2964K rodata, 532K init, 452K bss, 17936K reserved, 131072K cma-reserved, 0K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xa0800000 - 0xff000000   (1512 MB)
[    0.000000]     lowmem  : 0x80000000 - 0xa0000000   ( 512 MB)
[    0.000000]     pkmap   : 0x7fe00000 - 0x80000000   (   2 MB)
[    0.000000]     modules : 0x7f000000 - 0x7fe00000   (  14 MB)
[    0.000000]       .text : 0x80008000 - 0x80b421a4   (11497 kB)
[    0.000000]       .init : 0x80b43000 - 0x80bc8000   ( 532 kB)
[    0.000000]       .data : 0x80bc8000 - 0x80c39960   ( 455 kB)
[    0.000000]        .bss : 0x80c3c000 - 0x80cad160   ( 453 kB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Preemptible hierarchical RCU implementation.
[    0.000000]  Additional per-CPU info printed with stalls.
[    0.000000]  RCU restricting CPUs from NR_CPUS=4 to nr_cpu_ids=1.
[    0.000000] RCU: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS:16 nr_irqs:16 16
[    0.000000] mxc_clocksource_init 3000000
[    0.000000] Switching to timer-based delay loop, resolution 333ns
[    0.000016] sched_clock: 32 bits at 3000kHz, resolution 333ns, wraps every 715827882841ns
[    0.000060] clocksource mxc_timer1: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 637086815595 ns
[    0.002858] Console: colour dummy device 80x30
[    0.002896] Calibrating delay loop (skipped), value calculated using timer frequency.. 6.00 BogoMIPS (lpj=30000)
[    0.002933] pid_max: default: 32768 minimum: 301
[    0.003163] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.003196] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.004575] CPU: Testing write buffer coherency: ok
[    0.005077] /cpus/cpu@0 missing clock-frequency property
[    0.005120] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000
[    0.005231] Setting up static identity map for 0x80008280 - 0x800082d8
[    0.041236] Brought up 1 CPUs
[    0.041275] SMP: Total of 1 processors activated (6.00 BogoMIPS).
[    0.041298] CPU: All CPU(s) started in SVC mode.
[    0.042406] devtmpfs: initialized
[    0.059997] device-tree: Duplicate name in lcdif@021c8000, renamed to "display#1"
[    0.066655] VFP support v0.3: implementor 41 architecture 2 part 30 variant 7 rev 5
[    0.067557] clocksource jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.077416] pinctrl core: initialized pinctrl subsystem
[    0.080101] NET: Registered protocol family 16
[    0.099346] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.131012] cpuidle: using governor ladder
[    0.161086] cpuidle: using governor menu
[    0.212072] hw-breakpoint: found 5 (+1 reserved) breakpoint and 4 watchpoint registers.
[    0.212108] hw-breakpoint: maximum watchpoint size is 8 bytes.
[    0.216664] imx6ul-pinctrl 20e0000.iomuxc: initialized IMX pinctrl driver
[    0.217474] imx6ul-pinctrl 2290000.iomuxc-snvs: initialized IMX pinctrl driver
[    0.289005] mxs-dma 1804000.dma-apbh: initialized
[    0.296326] SCSI subsystem initialized
[    0.297682] usbcore: registered new interface driver usbfs
[    0.297861] usbcore: registered new interface driver hub
[    0.298078] usbcore: registered new device driver usb
[    0.301111] i2c i2c-0: IMX I2C adapter registered
[    0.301162] i2c i2c-0: can't use DMA
[    0.303189] i2c i2c-1: IMX I2C adapter registered
[    0.303238] i2c i2c-1: can't use DMA
[    0.303565] Linux video capture interface: v2.00
[    0.303768] pps_core: LinuxPPS API ver. 1 registered
[    0.303794] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    0.303871] PTP clock support registered
[    0.306957] MIPI CSI2 driver module loaded
[    0.307659] Advanced Linux Sound Architecture Driver Initialized.
[    0.309938] Bluetooth: Core ver 2.20
[    0.310070] NET: Registered protocol family 31
[    0.310093] Bluetooth: HCI device and connection manager initialized
[    0.310134] Bluetooth: HCI socket layer initialized
[    0.310170] Bluetooth: L2CAP socket layer initialized
[    0.310259] Bluetooth: SCO socket layer initialized
[    0.312975] Switched to clocksource mxc_timer1
[    0.340443] NET: Registered protocol family 2
[    0.342065] TCP established hash table entries: 4096 (order: 2, 16384 bytes)
[    0.342209] TCP bind hash table entries: 4096 (order: 3, 32768 bytes)
[    0.342371] TCP: Hash tables configured (established 4096 bind 4096)
[    0.342510] UDP hash table entries: 256 (order: 1, 8192 bytes)
[    0.342573] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
[    0.343152] NET: Registered protocol family 1
[    0.343891] RPC: Registered named UNIX socket transport module.
[    0.343923] RPC: Registered udp transport module.
[    0.343943] RPC: Registered tcp transport module.
[    0.343961] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    0.347514] imx rpmsg driver is registered.
[    0.350439] Bus freq driver module loaded
[    0.352666] futex hash table entries: 256 (order: 2, 16384 bytes)
[    0.370194] VFS: Disk quotas dquot_6.6.0
[    0.370705] VFS: Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
[    0.376648] NFS: Registering the id_resolver key type
[    0.376751] Key type id_resolver registered
[    0.376773] Key type id_legacy registered
[    0.376957] jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
[    0.378912] fuse init (API version 7.23)
[    0.390403] io scheduler noop registered
[    0.390452] io scheduler deadline registered
[    0.390976] io scheduler cfq registered (default)
[    0.392154] imx-weim 21b8000.weim: Driver registered.
[    0.399030] backlight supply power not found, using dummy regulator
[    0.401600] MIPI DSI driver module loaded
[    0.402126] MIPI DSI driver module loaded
[    0.408475] 21c8000.lcdif supply lcd not found, using dummy regulator
[    0.517551] mxsfb 21c8000.lcdif: failed to find mxc display driver
[    0.528119] Console: switching to colour frame buffer device 100x30
[    0.542141] mxsfb 21c8000.lcdif: initialized
[    0.544488] imx-sdma 20ec000.sdma: no event needs to be remapped
[    0.544713] imx-sdma 20ec000.sdma: loaded firmware 3.3
[    0.552047] imx-sdma 20ec000.sdma: initialized
[    0.554543] 2020000.serial: ttymxc0 at MMIO 0x2020000 (irq = 19, base_baud = 5000000) is a IMX
[    1.254074] console [ttymxc0] enabled
[    1.259780] 21ec000.serial: ttymxc2 at MMIO 0x21ec000 (irq = 233, base_baud = 5000000) is a IMX
[    1.273085] ap3216c 0-001e: ap3216c init failed
[    1.277700] ap3216c: probe of 0-001e failed with error -1
[    1.316987] imx-rng 2284000.rngb: iMX RNG Registered.
[    1.322796] imx sema4 driver is registered.
[    1.327295] [drm] Initialized drm 1.1.0 20060810
[    1.333381] [drm] Initialized vivante 1.0.0 20120216 on minor 0
[    1.365309] brd: module loaded
[    1.381555] loop: module loaded
[    1.390521] fsl-quadspi 21e0000.qspi: unrecognized JEDEC id bytes: ff, ff, ff
[    1.397908] fsl-quadspi 21e0000.qspi: Freescale QuadSPI probe failed
[    1.410515] spi_imx 2010000.ecspi: probed
[    1.417516] CAN device driver interface
[    1.424647] flexcan 2090000.can: device registered (reg_base=a0a00000, irq=26)
[    1.434385] 20b4000.ethernet supply phy not found, using dummy regulator
[    1.644218] pps pps0: new PPS source ptp0
[    1.650785] libphy: fec_enet_mii_bus: probed
[    1.658517] fec 20b4000.ethernet eth0: registered PHC device 0
[    1.666334] 2188000.ethernet supply phy not found, using dummy regulator
[    1.884082] pps pps1: new PPS source ptp1
[    1.891785] fec 2188000.ethernet eth1: registered PHC device 1
[    1.899597] PPP generic driver version 2.4.2
[    1.904567] PPP BSD Compression module registered
[    1.909327] PPP Deflate Compression module registered
[    1.914532] PPP MPPE Compression module registered
[    1.919385] NET: Registered protocol family 24
[    1.924217] usbcore: registered new interface driver asix
[    1.929814] usbcore: registered new interface driver ax88179_178a
[    1.936258] usbcore: registered new interface driver cdc_ether
[    1.942266] usbcore: registered new interface driver net1080
[    1.948186] usbcore: registered new interface driver cdc_subset
[    1.954336] usbcore: registered new interface driver zaurus
[    1.960186] usbcore: registered new interface driver cdc_ncm
[    1.965949] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    1.972521] ehci-mxc: Freescale On-Chip EHCI Host driver
[    1.978426] usbcore: registered new interface driver usb-storage
[    1.984707] usbcore: registered new interface driver usb_ehset_test
[    1.993906] 2184800.usbmisc supply vbus-wakeup not found, using dummy regulator
[    2.003247] 2184000.usb supply vbus not found, using dummy regulator
[    2.017663] 2184200.usb supply vbus not found, using dummy regulator
[    2.028087] ci_hdrc ci_hdrc.1: EHCI Host Controller
[    2.033154] ci_hdrc ci_hdrc.1: new USB bus registered, assigned bus number 1
[    2.053045] ci_hdrc ci_hdrc.1: USB 2.0 started, EHCI 1.00
[    2.060728] hub 1-0:1.0: USB hub found
[    2.064804] hub 1-0:1.0: 1 port detected
[    2.071377] mousedev: PS/2 mouse device common for all mice
[    2.079132] input: 20cc000.snvs:snvs-powerkey as /devices/platform/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0
[    2.093733] <<-GTP-INFO->> GTP driver installing...
[    2.099335] <<-GTP-INFO->> GTP Driver Version: V2.4<2014/11/28>
[    2.105458] <<-GTP-INFO->> GTP I2C Address: 0x5d
[    2.110164] 1-005d supply vdd_ana not found, using dummy regulator
[    2.116587] 1-005d supply vcc_i2c not found, using dummy regulator
[    2.122998] <<-GTP-INFO->> Guitar reset
[    2.254175] <<-GTP-ERROR->> I2C Read: 0x8047, 1 bytes failed, errcode: -5! Process reset.
[    2.262388] <<-GTP-INFO->> Guitar reset
[    2.383003] <<-GTP-ERROR->> GTP i2c test failed time 1.
[    2.404108] <<-GTP-ERROR->> I2C Read: 0x8047, 1 bytes failed, errcode: -5! Process reset.
[    2.412312] <<-GTP-INFO->> Guitar reset
[    2.533023] <<-GTP-ERROR->> GTP i2c test failed time 2.
[    2.554093] <<-GTP-ERROR->> I2C Read: 0x8047, 1 bytes failed, errcode: -5! Process reset.
[    2.562296] <<-GTP-INFO->> Guitar reset
[    2.683001] <<-GTP-ERROR->> GTP i2c test failed time 3.
[    2.704093] <<-GTP-ERROR->> I2C Read: 0x8047, 1 bytes failed, errcode: -5! Process reset.
[    2.712295] <<-GTP-INFO->> Guitar reset
[    2.832995] <<-GTP-ERROR->> GTP i2c test failed time 4.
[    2.854096] <<-GTP-ERROR->> I2C Read: 0x8047, 1 bytes failed, errcode: -5! Process reset.
[    2.862300] <<-GTP-INFO->> Guitar reset
[    2.983000] <<-GTP-ERROR->> GTP i2c test failed time 5.
[    3.003000] <<-GTP-ERROR->> I2C communication ERROR!
[    3.008035] goodix-ts: probe of 1-005d failed with error -5
[    3.017932] snvs_rtc 20cc000.snvs:snvs-rtc-lp: rtc core: registered 20cc000.snvs:snvs-r as rtc0
[    3.027146] i2c /dev entries driver
[    3.033647] IR NEC protocol handler initialized
[    3.038241] IR RC5(x/sz) protocol handler initialized
[    3.043481] IR RC6 protocol handler initialized
[    3.048057] IR JVC protocol handler initialized
[    3.052623] IR Sony protocol handler initialized
[    3.057334] IR SANYO protocol handler initialized
[    3.062078] IR Sharp protocol handler initialized
[    3.066863] IR MCE Keyboard/mouse protocol handler initialized
[    3.072732] IR XMP protocol handler initialized
[    3.080668] pxp-v4l2 pxp_v4l2: initialized
[    3.086908] 0-000e supply vdd not found, using dummy regulator
[    3.092918] 0-000e supply vddio not found, using dummy regulator
[    3.099168] mag3110 0-000e: check mag3110 chip ID
[    3.104264] mag3110 0-000e: read chip ID 0xfffffffb is not equal to 0xc4!
[    3.111313] mag3110: probe of 0-000e failed with error -22
[    3.119387] imx2-wdt 20bc000.wdog: use WDOG_B to reboot.
[    3.125874] imx2-wdt 20bc000.wdog: timeout 60 sec (nowayout=0)
[    3.132135] Bluetooth: HCI UART driver ver 2.3
[    3.136892] Bluetooth: HCI UART protocol H4 registered
[    3.142073] Bluetooth: HCI UART protocol BCSP registered
[    3.147475] Bluetooth: HCI UART protocol ATH3K registered
[    3.153202] usbcore: registered new interface driver bcm203x
[    3.159094] usbcore: registered new interface driver btusb
[    3.164891] usbcore: registered new interface driver ath3k
[    3.171538] sdhci: Secure Digital Host Controller Interface driver
[    3.177864] sdhci: Copyright(c) Pierre Ossman
[    3.182272] sdhci-pltfm: SDHCI platform and OF driver helper
[    3.190560] /soc/aips-bus@02100000/usdhc@02190000: voltage-ranges unspecified
[    3.197901] sdhci-esdhc-imx 2190000.usdhc: Got CD GPIO
[    3.205328] sdhci-esdhc-imx 2190000.usdhc: No vqmmc regulator found
[    3.255507] mmc0: SDHCI controller on 2190000.usdhc [2190000.usdhc] using ADMA
[    3.264969] /soc/aips-bus@02100000/usdhc@02194000: voltage-ranges unspecified
[    3.273232] sdhci-esdhc-imx 2194000.usdhc: No vmmc regulator found
[    3.279472] sdhci-esdhc-imx 2194000.usdhc: No vqmmc regulator found
[    3.324121] mmc1: SDHCI controller on 2194000.usdhc [2194000.usdhc] using ADMA
[    3.381864] usbcore: registered new interface driver usbhid
[    3.387772] usbhid: USB HID core driver
[    3.401477] wm8960 1-001a: Failed to issue reset
[    3.406612] wm8960: probe of 1-001a failed with error -5
[    3.412073] mmc1: MAN_BKOPS_EN bit is not set
[    3.420022] fsl-asrc 2034000.asrc: driver registered
[    3.442108] imx-wm8960 sound: failed to find codec platform device
[    3.449573] imx-wm8960: probe of sound failed with error -22
[    3.465524] NET: Registered protocol family 26
[    3.471993] NET: Registered protocol family 10
[    3.485911] sit: IPv6 over IPv4 tunneling driver
[    3.492279] NET: Registered protocol family 17
[    3.501425] bridge: automatic filtering via arp/ip/ip6tables has been deprecated. Update your scripts to load br_netfilter if you need this.
[    3.515223] can: controller area network core (rev 20120528 abi 9)
[    3.523821] NET: Registered protocol family 29
[    3.528374] can: raw protocol (rev 20120528)
[    3.532689] can: broadcast manager protocol (rev 20120528 t)
[    3.539582] can: netlink gateway (rev 20130117) max_hops=1
[    3.552311] Bluetooth: RFCOMM TTY layer initialized
[    3.557495] mmc1: new HS200 MMC card at address 0001
[    3.563063] Bluetooth: RFCOMM socket layer initialized
[    3.568270] Bluetooth: RFCOMM ver 1.11
[    3.572096] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[    3.578119] mmcblk1: mmc1:0001 8GTF4R 7.28 GiB
[    3.583128] mmcblk1boot0: mmc1:0001 8GTF4R partition 1 4.00 MiB
[    3.589357] Bluetooth: BNEP filters: protocol multicast
[    3.594794] mmcblk1boot1: mmc1:0001 8GTF4R partition 2 4.00 MiB
[    3.601084] mmcblk1rpmb: mmc1:0001 8GTF4R partition 3 512 KiB
[    3.607079] Bluetooth: BNEP socket layer initialized
[    3.612113] Bluetooth: HIDP (Human Interface Emulation) ver 1.2
[    3.621613] Bluetooth: HIDP socket layer initialized
[    3.626930]  mmcblk1: p1 p2
[    3.630475] 8021q: 802.1Q VLAN Support v1.8
[    3.639765] Key type dns_resolver registered
[    3.671746] dhd_module_init in
[    3.676370] input: gpio_keys@0 as /devices/platform/gpio_keys@0/input/input1
[    3.684232] snvs_rtc 20cc000.snvs:snvs-rtc-lp: setting system clock to 1970-01-01 00:04:45 UTC (285)
[    3.913670] fec 20b4000.ethernet eth0: Freescale FEC PHY driver [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=20b4000.ethernet:01, irq=-1)
[    3.953092] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[   10.233414] fec 20b4000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
[   10.253047] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[   10.273327] IP-Config: Complete:
[   10.276592]      device=eth0, hwaddr=de:b4:7c:de:dc:07, ipaddr=192.168.0.121, mask=255.255.255.0, gw=192.168.0.1
[   10.286921]      host=192.168.0.121, domain=, nis-domain=(none)
[   10.292860]      bootserver=192.168.1.120, rootserver=192.168.0.120, rootpath=
[   10.300220] gpio_dvfs: disabling
[   10.303528] VSD_3V3: disabling
[   10.306596] can-3v3: disabling
[   10.309917] ALSA device list:
[   10.312897]   No soundcards found.
[   20.340820] VFS: Mounted root (nfs filesystem) on device 0:14.
[   20.347526] devtmpfs: mounted
[   20.351223] Freeing unused kernel memory: 532K (80b43000 - 80bc8000)
can't run '/etc/init.d/rcS': No such file or directory

Please press Enter to activate this console.

正常加载!
可以看出,我们进入了根文件系统,说明我们的根文件系统工作了!如果没有启动进入根文件系统的话可以重启一次开发板试试。我们可以输入“ls”命令测试一下,结果如下所示:

Please press Enter to activate this console.
/ # ls
bin      lib      mnt      root     sys      usr
dev      linuxrc  proc     sbin     tmp

可以看出 ls 命令工作正常!那么是不是说明我们的 rootfs 就制作成功了呢?大家注意,在进入根文件系统的时候会有下面这一行错误提示:

can't run '/etc/init.d/rcS': No such file or directory

提示很简单,说是无法运行“/etc/init.d/rcS”这个文件,因为这个文件不存在。

看来我们的 rootfs 还是缺文件啊,没什么说的,一步一步的完善吧。

五、完善根文件系统

1、创建/etc/init.d/rcS文件

 rcS 是个 shell 脚本,Linux 内核启动以后需要启动一些服务,而 rcS 就是规定启动哪些文件的脚本文件。在 rootfs 中创建/etc/init.d/rcS 文件,然后在 rcS 中输入如下所示内容:

#!/bin/sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
export PATH LD_LIBRARY_PATH

mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts

echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
  • 第 1 行,表示这是一个 shell 脚本。
  • 第 3 行,PATH 环境变量保存着可执行文件可能存在的目录,这样我们在执行一些命令或者可执行文件的时候就不会提示找不到文件这样的错误。
  • 第 4 行,LD_LIBRARY_PATH 环境变量保存着库文件所在的目录。
  • 第 5 行,使用 export 来导出上面这些环境变量,相当于声明一些“全局变量”。
  • 第 7 行,使用 mount 命令来挂载所有的文件系统,这些文件系统由文件/etc/fstab 来指定,所以我们一会还要创建/etc/fstab 文件。
  • 第 8 和 9 行,创建目录/dev/pts,然后将 devpts 挂载到/dev/pts 目录中。
  • 第 11 和 12 行,使用 mdev 来管理热插拔设备,通过这两行,Linux 内核就可以在/dev 目录下自动创建设备节点。关于 mdev 的详细内容可以参考 busybox 中的 docs/mdev.txt 文档。

 上面代码中的 rcS 文件内容是最精简的,大家如果去看 Ubuntu 或者其他大型 Linux操作系统中的 rcS 文件,就会发现其非常复杂。因为我们是初次学习,所以不用搞这么复杂的,而且这么复杂的 rcS 文件也是借助其他工具创建的,比如 buildroot 等。
创建好文件/etc/init.d/rcS 以后一定要给其可执行权限!
使用如下命令给予/ec/init.d/rcS 可执行权限:

chmod 777 rcS

 设置好以后就重新启动 Linux 内核,启动以后如下所示:

[   20.331805] VFS: Mounted root (nfs filesystem) on device 0:14.
[   20.338481] devtmpfs: mounted
[   20.342175] Freeing unused kernel memory: 532K (80b43000 - 80bc8000)
mount: can't read '/etc/fstab': No such file or directory
/etc/init.d/rcS: line 11: can't create /proc/sys/kernel/hotplug: nonexistent directory
mdev: /sys/dev: No such file or directory

Please press Enter to activate this console.

 可以看到,提示找不到/etc/fstab 文件,还有一些其他的错误,我们先把/etc/fstab这个错误解决了。说不定把这个问题解决以后其他的错误也就解决了。前面我们说了“mount -a”挂载所有根文件系统的时候需要读取/etc/fstab,因为/etc、fstab 里面定义了该挂载哪些文件,好了,接下来就是创建/etc/fstab 文件。

2、创建/etc/fstab文件

 在 rootfs中创建/etc/fstab 文件,fstab在Linux开机以后自动配置哪些需要自动挂载的分区,格式如下:

<file system> <mount point> <type> <options> <dump> <pass>
  • <file system>:要挂载的特殊的设备,也可以是块设备,比如/dev/sda 等等。
  • <mount point>:挂载点。
  • <type>:文件系统类型,比如 ext2、ext3、proc、romfs、tmpfs 等等。
  • <options>:挂载选项,在 Ubuntu 中输入“man mount”命令可以查看具体的选项。一般使用 defaults,也就是默认选项,defaults 包含了 rw、suid、 dev、 exec、 auto、 nouser 和 async。
  • <dump>:为 1 的话表示允许备份,为 0 不备份,一般不备份,因此设置为 0。
  • <pass>:磁盘检查设置,为 0 表示不检查。根目录‘/’设置为 1,其他的都不能设置为 1,其他的分区从 2 开始。一般不在 fstab 中挂载根目录,因此这里一般设置为 0。

按照上述格式,在 fstab 文件中输入如下内容:

vim fstab
#<file system> <mount point> <type> <options> <dump> <pass>
proc           /proc         proc   defaults  0      0
tmpfs          /tmp          tmpfs  defaults  0      0
sysfs          /sys          sysfs  defaults  0      0

fstab 文件创建完成以后重新启动 Linux,结果如下所示:

[   20.331928] VFS: Mounted root (nfs filesystem) on device 0:14.
[   20.338597] devtmpfs: mounted
[   20.342293] Freeing unused kernel memory: 532K (80b43000 - 80bc8000)

Please press Enter to activate this console.

 从上面可以看出,启动成功,而且没有任何错误提示。但是我们要还需要创建一个文件/etc/inittab。

3、创建/etc/inittab文件

 inittab 的详细内容可以参考 busybox 下的文件 examples/inittab。init 程序会读取/etc/inittab这个文件,inittab 由若干条指令组成。每条指令的结构都是一样的,由以“:”分隔的 4 个段组成,格式如下:

<id>:<runlevels>:<action>:<process>
  • <id>:每个指令的标识符,不能重复。但是对于 busybox 的 init 来说,有着特殊意义。对于 busybox 而言用来指定启动进程的控制 tty,一般我们将串口或者 LCD 屏幕设置为控制 tty。
  • <runlevels> :对 busybox 来说此项完全没用,所以空着。
  • <action>:动作,用于指定可能用到的动作。busybox 支持的动作如下表所示:
动作描述
sysinit在系统初始化的时候 process 才会执行一次。
respawn当 process 终止以后马上启动一个新的。
askfirst和 respawn类似,在运行 process 之前在控制台上显示“Please press Enter to activate this console.”。只要用户按下“Enter”键以后才会执行 process。
wait告诉 init,要等待相应的进程执行完以后才能继续执行。
once仅执行一次,而且不会等待 process 执行完成。
restart当 init 重启的时候才会执行 procee。
ctrlaltdel当按下 ctrl+alt+del 组合键才会执行 process。
shutdown关机的时候执行 process。
  • <process> :具体的动作,比如程序、脚本或命令等。

 参考 busybox 的 examples/inittab 文件,我们也创建一个/etc/inittab,在里面输入如下内容:

#etc/inittab
::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
  • 第 2 行,系统启动以后运行/etc/init.d/rcS 这个脚本文件。
  • 第 3 行,将 console 作为控制台终端,也就是 ttymxc0。
  • 第 4 行,重启的话运行/sbin/init。
  • 第 5 行,按下 ctrl+alt+del 组合键的话就运行/sbin/reboot,看来 ctrl+alt+del 组合键用于重启系统。
  • 第 6 行,关机的时候执行/bin/umount,也就是卸载各个文件系统。
  • 第 7 行,关机的时候执行/sbin/swapoff,也就是关闭交换分区。

 /etc/initta 文件创建好以后就可以重启开发板即可,至此!根文件系统要创建的文件就已经全部完成了。

 接下来就要对根文件系统进行其他的测试,比如是我们自己编写的软件运行是否正常、是否支持软件开机自启动、中文支持是否正常以及能不能链接等。

六、根文件系统其他功能测试

1、软件运行测试

 我们使用 Linux 的目的就是运行我们自己的软件,我们编译的应用软件一般都使用动态库,使用动态库的话应用软件体积就很小,但是得提供库文件,库文件我们已经添加到了根文件系统中。我们编写一个小小的测试软件来测试一下库文件是否工作正常,在根文件系统下创建一个名为“drivers”的文件夹,以后我们学习 Linux 驱动的时候就把所有的实验文件放到这个文件夹里面。

 在 ubuntu 下使用 vim 编辑器新建一个 hello.c 文件,在 hello.c 里面输入如下内容:

hello.c

#include <stdio.h>

int main(void)
{
	while(1) {
		printf("hello world!\r\n");
		sleep(2);
	}
	return 0;
}

 hello.c 内容很简单,就是循环输出“hello world”,sleep 相当于 Linux 的延时函数,单位为秒,所以 sleep(2)就是延时 2 秒。编写好以后就是编译,因为我们是要在 ARM 芯片上运行的,所以要用交叉编译器去编译,也就是使用 arm-linux-gnueabihf-gcc 编译,命令如下:

arm-linux-gnueabihf-gcc hello.c -o hello

 使用 arm-linux-gnueabihf-gcc 将 hello.c 编译为 hello 可执行文件。这个 hello 可执行文件究竟是不是 ARM 使用的呢?使用“file”命令查看文件类型以及编码格式:

file hello //查看 hello 的文件类型以及编码格式
liefyuan@ubuntu:~/linux/nfs/rootfs/drivers/hello$ arm-linux-gnueabihf-gcc hello.c -o hello
liefyuan@ubuntu:~/linux/nfs/rootfs/drivers/hello$ ls
hello  hello.c
liefyuan@ubuntu:~/linux/nfs/rootfs/drivers/hello$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.31, BuildID[sha1]=7dd1bde89e09327b11ad95e22e72f9bfafd8aecb, not stripped

 可以看出,输入“file hello”输入了如下所示信息:

hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked……

 hello 是个 32 位的 LSB 可执行文件,ARM 架构的,并且是动态链接的。所以我们编译出来的 hello 文件没有问题。将其拷贝到 rootfs/drivers 目录下,在开发板中输入如下命令来执行这个可执行文件:

cd /drivers  //进入 drivers 目录
./hello //执行 hello
/drivers/hello # ls
hello    hello.c
/drivers/hello # ./hello
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
^C
/drivers/hello #

 可以看出,hello 这个软件运行正常,说明我们的根文件系统中的共享库是没问题的,要想终止 hello 的运行,按下“ctrl+c”组合键即可。此时大家应该能感觉到,hello 执行的时候终端是没法用的,除非使用“ctrl+c”来关闭 hello,那么有没有办法既能让 hello 正常运行,而且终端能够正常使用?那肯定是有的,让 hello 进入后台运行就行了,让一个软件进入后台的方法很简单,运行软件的时候加上“&”即可,比如“./hello &”就是让 hello 在后台运行。在后台运
行的软件可以使用“kill -9 pid(进程 ID)”命令来关闭掉,首先使用“ps”命令查看要关闭的软件 PID 是多少,ps 命令用于查看所有当前正在运行的进程,并且会给出进程的 PID。输入“ps”命令,结果如下所示:

/drivers/hello # ./hello &
/drivers/hello # hello world!
hello world!
hello world!
hello world!
^C
/drivers/hello # hello world!
hello world!
hello world!
hello world!
^C
/drivers/hello # hello world!
hello world!
hello world!
hello world!
ps
PID   USER     TIME  COMMAND
    1 0         0:02 init
    2 0         0:00 [kthreadd]
    3 0         0:00 [ksoftirqd/0]
    4 0         0:00 [kworker/0:0]
    5 0         0:00 [kworker/0:0H]
    6 0         0:00 [kworker/u2:0]
    7 0         0:00 [rcu_preempt]
    8 0         0:00 [rcu_sched]
    9 0         0:00 [rcu_bh]
   10 0         0:00 [migration/0]
   11 0         0:00 [khelper]
   12 0         0:00 [kdevtmpfs]
   13 0         0:00 [perf]
   14 0         0:00 [writeback]
   15 0         0:00 [crypto]
   16 0         0:00 [bioset]
   17 0         0:00 [kblockd]
   18 0         0:00 [ata_sff]
   19 0         0:00 [kworker/0:1]
   20 0         0:00 [cfg80211]
   21 0         0:00 [rpciod]
   22 0         0:00 [kswapd0]
   23 0         0:00 [fsnotify_mark]
   24 0         0:00 [nfsiod]
   61 0         0:00 [spi2]
   62 0         0:00 [kworker/u2:1]
   65 0         0:00 [ci_otg]
   66 0         0:00 [goodix_wq]
   67 0         0:00 [cfinteractive]
   68 0         0:00 [irq/223-mmc0]
   69 0         0:00 [irq/48-2190000.]
   70 0         0:00 [irq/224-mmc1]
   71 0         0:00 [mxs_dcp_chan/sh]
   72 0         0:00 [mxs_dcp_chan/ae]
   77 0         0:00 [ipv6_addrconf]
   78 0         0:00 [krfcommd]
   79 0         0:00 [mmcqd/1]
   80 0         0:00 [mmcqd/1boot0]
   81 0         0:00 [mmcqd/1boot1]
   82 0         0:00 [mmcqd/1rpmb]
   83 0         0:00 [pxp_dispatch]
   84 0         0:00 [deferwq]
   85 0         0:00 [irq/203-imx_the]
   86 0         0:00 [kworker/0:2]
   87 0         0:00 [kworker/0:3]
   88 0         0:00 [kworker/0:1H]
   94 0         0:00 -/bin/sh
  106 0         0:00 ./hello
  107 0         0:00 ps
/drivers/hello # hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
killhello world!
hello world!
 -hello world!
9hello world!
hello world!
hello world!
 hello world!
106hello world!
/drivers/hello # kill -9 106

 可以看出 hello 对应的 PID 为 166,因此我们使用如下命令关闭在后台运行的hello 软件:

kill -9 106

因为 hello 在不断的输出“hello world”所以我们的输入看起来会被打断,其实是没有的,因为我们是输入,而 hello 是输出。在数据流上是没有打断的,只是显示在 SecureCRT 上就好像被打断了,所以只管输入“kill -9 166”即可。hello 被 kill 以后会有提示。

/drivers/hello # ps
PID   USER     TIME  COMMAND
    1 0         0:02 init
    2 0         0:00 [kthreadd]
    3 0         0:00 [ksoftirqd/0]
    5 0         0:00 [kworker/0:0H]
    6 0         0:00 [kworker/u2:0]
    7 0         0:00 [rcu_preempt]
    8 0         0:00 [rcu_sched]
    9 0         0:00 [rcu_bh]
   10 0         0:00 [migration/0]
   11 0         0:00 [khelper]
   12 0         0:00 [kdevtmpfs]
   13 0         0:00 [perf]
   14 0         0:00 [writeback]
   15 0         0:00 [crypto]
   16 0         0:00 [bioset]
   17 0         0:00 [kblockd]
   18 0         0:00 [ata_sff]
   19 0         0:00 [kworker/0:1]
   20 0         0:00 [cfg80211]
   21 0         0:00 [rpciod]
   22 0         0:00 [kswapd0]
   23 0         0:00 [fsnotify_mark]
   24 0         0:00 [nfsiod]
   61 0         0:00 [spi2]
   62 0         0:00 [kworker/u2:1]
   65 0         0:00 [ci_otg]
   66 0         0:00 [goodix_wq]
   67 0         0:00 [cfinteractive]
   68 0         0:00 [irq/223-mmc0]
   69 0         0:00 [irq/48-2190000.]
   70 0         0:00 [irq/224-mmc1]
   71 0         0:00 [mxs_dcp_chan/sh]
   72 0         0:00 [mxs_dcp_chan/ae]
   77 0         0:00 [ipv6_addrconf]
   78 0         0:00 [krfcommd]
   79 0         0:00 [mmcqd/1]
   80 0         0:00 [mmcqd/1boot0]
   81 0         0:00 [mmcqd/1boot1]
   82 0         0:00 [mmcqd/1rpmb]
   83 0         0:00 [pxp_dispatch]
   84 0         0:00 [deferwq]
   85 0         0:00 [irq/203-imx_the]
   86 0         0:00 [kworker/0:2]
   88 0         0:00 [kworker/0:1H]
   94 0         0:00 -/bin/sh
  108 0         0:00 ps
[1]+  Killed                     ./hello

 再去用 ps 命令查看一下当前的进程,发现没有 hello 了。这个就是 Linux 下的软件后台运行以及如何关闭软件的方法,重点就是 3 个操作:

  • 软件后面加“&”
  • 使用 ps 查看要关闭的软件 PID
  • 使用“kill -9 pid”来关闭指定的软件。

2、开机自启动测试

 在测试 hello 软件的时候都是等 Linux 启动进入根文件系统以后手动输入命令“./hello”来完成的。我们一般做好产品以后都是需要开机自动启动相应的软件,本节我们就以hello 这个软件为例,讲解一下如何实现开机自启动。前面我们说过了,进入根文件系统的时候会运行/etc/init.d/rcS 这个 shell 脚本,因此我们可以在这个脚本里面添加自启动相关内容。添加完成以后的/etc/init.d/rcS 文件内容如下:

#!/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
runlevel=S
umask 022
export PATH LD_LIBRARY_PATH runlevel

mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts

echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

#开机自启动
cd /drivers
./hello &
cd /
  • 第 16 行,进入 drivers 目录,因为要启动的软件存放在 drivers 目录下。
  • 第 17 行,以后台方式执行 hello 这个软件。
  • 第 18 行,退出 drivers 目录,进入到根目录下。

 自启动代码添加完成以后就可以重启开发板,看看 hello 这个软件会不会自动运行。结果如下所示:

[   10.300224] gpio_dvfs: disabling
[   10.303536] VSD_3V3: disabling
[   10.306608] can-3v3: disabling
[   10.309926] ALSA device list:
[   10.312904]   No soundcards found.
[   20.331734] VFS: Mounted root (nfs filesystem) on device 0:14.
[   20.338404] devtmpfs: mounted
[   20.342098] Freeing unused kernel memory: 532K (80b43000 - 80bc8000)

Please press Enter to activate this console. hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!
hello world!

可以看出,hello 开机自动运行了,说明开机自启动成功。

3、外网连接测试

 这里说的外网不是外国哪些 404 网站的连接测试,而是百度、淘宝等这些网站的测试。也就是说看看我们的开发板能不能上网,能不能和我们的局域网外的这些网站进行通信。测试方法很简单,就是通过 ping 命令来 ping 一下百度的官网:www.baidu.com。输入如下命令:

ping www.baidu.com

结果如下所示:

/ # ping www.baidu.com
ping: bad address 'www.baidu.com'

 可以看出,测试失败,提示 www.baidu.com 是个“bad address”,也就是地址不对,显然我们的地址是正确的。之所以出现这个错误提示是因为 www.baidu.com 的地址解析失败了,并没有解析出其对应的 IP 地址。我们需要配置域名解析服务器的 IP 地址,一般域名解析地址可以设置为所处网络的网关地址,比如 192.168.1.1。也可以设置为 114.114.1144.114,这个是运营商的域名解析服务器地址。

 在 rootfs 中新建文件/etc/resolv.conf,然后在里面输入如下内容:

nameserver 114.114.114.114
nameserver 192.168.1.1

 可以看出 ping 百度官网成功了!域名也成功的解析了,至此!我们的根文件系统就彻底的制作完成,这个根文件系统最好打包保存一下,防止以后做实验不小心破坏了根文件系统而功亏一篑,又得从头制作根文件系统。uboot、Linux kernel、rootfs 这三个共同构成了一个完整的Linux 系统,现在的系统至少是一个可以正常运行的系统,后面我们就可以在这个系统上完成Linux 驱动开发的学习。

七、打包根文件系统

 自己构建的根文件系统 rootfs,这里我们需要对 rootfs 进行打包,进入到 Ubuntu 中的rootfs 目录中,然后使用 tar 命令对其进行打包,命令如下:

cd rootfs/
tar -vcjf rootfs.tar.bz2 *

 完成以后会在 rootfs 目录下生成一个名为 rootfs.tar.bz2 的压缩包,将 rootfs.tar.bz2 发送到windows 系统中。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页