[ARM Linux系统移植] 根文件系统构建

linux 移植“三巨头”,目前已经完成了两个:u-boot 和 linux 内核。本篇就说明剩下的最后一个——根文件系统。

根文件系统由一堆系统所需要的文件构成,我们这里使用 busybox 来构建,使用的版本为 1.29.0。

1. 编译 busybox

首先我们修改 busybox 的顶层 makefile,指定 CROSS_COMPILEARCH 的值,这和之前的 u-boot、linux 的移植是一样的。CROSS_COMPILE 这边我们使用绝对路径,确保编译不出错。

  1. CROSS_COMPILE ?= /home/tim/tool/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
  2. ……
  3. ARCH ?= arm

接着设置配置选项:

  • > make defconfig
  • > make menuconfig

在图形化配置界面中,我们需要确认和更改以下选项:

Settings - Build static binary (no shared libs):确认这个选项没有开启。需要使用动态编译,静态编译会使得编译出的文件非常大。

Settings - vi-style line editing commands:开启这个选项。

Linux Module Utilities - Simplified modutils:取消这个选项。

Linux System Utilities - mdev (16 kb):确认这个选项以及子目录下的选项均已开启。

Settings - Support Unicode - Check $LC_ALL, $LC_CTYPE and $LANG environment variables:开启这个选项。

保存好配置之后,就可以开始编译了,执行以下命令:

  • > make
  • > make install CONFIG_PREFIX=/home/time/nfs/rootfs

其中,CONFIG_PREFIX 指定编译结果安装的目录,这里我们直接安装到 nfs 服务器所在目录。

2. 添加库文件

由于我们是动态编译 busybox 的,所以需要将交叉编译环境的库文件拷贝到第 1 节中安装的根文件系统中。

lib 目录

首先我们在根文件系统中创建 lib 目录:

  • > cd /home/time/nfs/rootfs
  • > mkdir lib

将交叉编译环境 libc/lib 下的文件拷贝其中:

  • > cd gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib
  • > cp *so* *.a /home/time/nfs/rootfs/lib/ -d

cp 指令的 -d 选项表示拷贝符号链接。这边有个比较特殊的库文件 ld-linux-armhf.so.3,它是一个软链接,但是 ld-linux-armhf.so.3 不能作为符号链接,否则在根文件系统中无法执行。

需要做的是,先将 rootfs/lib 下的删除:

  • > rm ld-linux-armhf.so.3

然后不带 -d 选项重新复制:

  • > cp ld-linux-armhf.so.3 /home/time/nfs/rootfs/lib/

接着同样拷贝交叉编译环境下的 lib 目录:

  • > cd gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib
  • > cp *so* *.a /home/time/nfs/rootfs/lib/ -d

至此,rootfs/lib 目录就“制作”完毕了。

usr/lib 目录

首先我们在根文件系统中创建 usr/lib 目录:

  • > cd /home/time/nfs/rootfs
  • > mkdir usr/lib

接着将交叉编译环境下 libc/usr/lib 下内容拷贝其中:

  • > cd gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
  • > cp *so* *.a /home/time/nfs/rootfs/usr/lib/ -d

3. 根文件系统测试

接下来我们先初步测试一下制作的根文件系统系统是否可行。在此之前,我们再建立一下常用的文件夹,建立好的所有文件夹如下图所示:

图1 各文件夹

我们使用 NFS 挂载根文件系统,需要设置的参数如下:

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>

在 u-boot 命令行模式下,重新设置 bootargs 环境变量如下:

  • > setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=169.254.155.83:/home/tim/nfs/rootfs,proto=tcp rw ip=169.254.155.84:169.254.155.83:169.254.1.1:255.255.0.0::eth0:off'
  • > saveenv

参数中一些 ip 地址的设置,便不再赘述。proto=tcp 表示使用 tcp 协议,rw 表示根文件系统可读可写;eth0 是网卡名,对应开发板上的 ENET2 网口。

设置好后重启开发板,就可以进入到文件系统了。但是会有

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

的提示。即虽然根文件系统可以正常使用了,但还需要进一步的完善。

4. 完善根文件系统

创建 etc/init.d/rcS 文件

linux 启动之后需要启动一些服务,rcS 就是规定启动哪些文件的脚本文件。我们创建 etc/init.d/rcS 文件,内容如下:

etc/init.d/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

注意需要赋予其可执行权限:

  • > chmod 777 rcS

创建 etc/fstab 文件

fstab 在 linux 开机以后自动配置哪些需要自动挂载的分区。创建的内容如下:

etc/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

创建 etc/inittab 文件

linux init 程序会读取 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

5. 根文件系统测试

最后,我们编译一个简单的 "hello world" 程序,以此验证根文件系统中的库文件是否正常。编写 hello.c 如下:

  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5.     while (1)
  6.     {
  7.         printf("Hello World!\r\n");
  8.         sleep(2);
  9.     }
  10.     return 0;
  11. }

交叉编译:

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

在开发板上运行,如果正常输出 ""Hello World!" 字样,就代表根文件系统相应的库文件正常。

结尾

翻找之前做的笔记,移植部分的第一篇笔记是 2020-12-6 开始写的。这一下子都过去八个月了,学习过程拉的着实有点长。这篇文章就算是移植部分的结束了,但有些恍惚,感觉什么都没学,又忘记当初为什么学了。

转而想了又想,也算是终于要开始 linux 驱动学习的重头戏了。希望之后一步一步实现自己的目标,继续努力!