LV09-03-linux-02-linux内核移植

本文主要是linux——内核移植的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
Linux开发板 华清远见 底板: FS4412_DEV_V5 核心板: FS4412 V2
u-boot 2013.01
点击查看本文参考资料
参考方向参考原文
Linux内核移植实验7 Linux内核移植
网卡驱动移植实验8 网卡驱动移植
根文件系统移植实验9 根文件系统移植
驱动开发指南i.MX6ULL Linux阿尔法开发板资料
Linux内核官网The Linux Kernel Archives
BusyBox官网BusyBox
点击查看相关文件下载

一、文件准备

1. linux内核源码

1.1 源码获取途径

关于linux内核的官方源码我们可以从这里获取:Index of /pub/linux/kernel/

1.2 获取源码文件

我下载的是linux-3.14.tar.xz这个压缩包并将其存放于ubuntu下的共享文件夹下,路径为:

1
/mnt/hgfs/Sharedfiles/test

1.3 拷贝解压

然后我们可以将这个源码文件拷贝解压到ubuntu中,方便后边编译,我们可以在终端执行以下命令:

1
2
3
cp /mnt/hgfs/Sharedfiles/test/linux-3.14.tar.xz ~/6temp/    # 拷贝压缩包
cd ~/6temp/
tar -xvf linux-3.14.tar.xz # 解压

然后我们可以得到下边这些文件(通过ls命令查看):

image-20220821125151346
点击查看部分文件说明
  • 平台相关

即与CPU架构或开发板硬件相关的源码,硬件的改动对应的代码也需要进行修改。

arch 与CPU架构相关的源代码
  • 平台无关
block磁盘设备的支持,就是块设备相关目录
crypto加密相关
drivers设备驱动
Documentation 帮助文档
firmware固件
fs文件系统
include头文件
init内核初始化
ipc进程间通信
kernel内核核心调度机制等
lib库相关目录
mm内存管理
net网络协议
samples例程
scripts工具、脚本等
security安全
sound音频处理
tools工具
usr打包与压缩,与initramfs相关的目录,用于生成initramfs
virt提供虚拟机技术(KVM)

还有一些其他的文件,这里就不再写了。

2. Busybox源码

2.1 源码获取途径

我们可以从这里获取Busybox的源码:Index of /downloads (busybox.net)

2.2 获取源码文件

我下载的是busybox-1.22.1.tar.bz2这个压缩包并将其存放于ubuntu下的共享文件夹下,路径为:

1
/mnt/hgfs/Sharedfiles/test

2.3 拷贝解压

然后我们可以将这个源码文件拷贝解压到ubuntu中,方便后边编译,我们可以在终端执行以下命令:

1
2
3
cp /mnt/hgfs/Sharedfiles/test/busybox-1.22.1.tar.bz2 ~/6temp/    # 拷贝压缩包
cd ~/6temp/
tar -xvf busybox-1.22.1.tar.bz2 # 解压

然后我们可以得到下边这些文件(通过ls命令查看):

image-20220821125437145

3. rootfs

3.1 文件获取

这里是直接制作好了的根文件系统相关文件有些文件可以直接从这里边拷贝,我们直接从这里下载并解压:根文件系统(roofs.rar,通过nfs共享给开发板的时候用)

下载完毕后,存放于ubuntu的共享文件夹中:

1
/mnt/hgfs/Sharedfiles/test

我们可直接在windows下将压缩文件解压,我们将得到一个名为rootfs.tar.bz2的压缩包

3.2 拷贝解压

然后我们可以将这个文件拷贝解压到ubuntu中,方便后边编译,我们可以在终端执行以下命令:

1
2
3
4
cp /mnt/hgfs/Sharedfiles/test/roofs/rootfs.tar.bz2 ~/6temp/    # 拷贝压缩包
cd ~/6temp/
mkdir ~/6temp/rootfs
tar -xvf rootfs.tar.bz2 -C rootfs # 解压

然后我们可以得到下边这些文件(通过ls命令查看):

image-20220821130122961
点击查看部分目录说明
binshell命令(elf格式)(通过busybox编译生成)
dev设备文件(内核启动后会将设备信息写入该目录)
etc内核配置文件
lib共享库(elf格式)(从交叉编译工具链中获取)
linuxrc内核运行的第一个应用程序(通过busybox编译生成)
mnt挂载目录(非必要)
proc进程相关文件(内核启动后会将进程信息写入该目录)
root超级用户家目录(非必要)
sbin系统管理shell命令(elf格式)(通过busybox编译生成)
sys驱动相关文件(内核启动后会将驱动信息写入该目录)
usrshell命令(elf格式)(通过busybox编译生成)

二、Linux内核移植

首先我们要进入Linux源码目录,后边所有的命令都在源码的顶层目录下进行:

1
cd ~/6temp/linux-3.14/

1. 指定交叉编译工具

linux内核源码并不知道我们使用的处理器架构及交叉编译工具是什么,这里我们需要自己在Makefile中指定,我们通过下边的命令打开顶层Makefile文件:

1
vim Makefile

找到下边的语句(这个3.14版本的话应该是在198行):

1
2
ARCH            ?= $(SUBARCH)
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)

将其修改为:

1
2
ARCH            ?= arm
CROSS_COMPILE ?= arm-none-linux-gnueabi-

【注意】arm-none-linux-gnueabi-后边不要有多余的空格。

2. linux内核编译

2.1 内核配置

2.1.1 配置文件

linux内核源码的arch/arm/configs目录下下对各个厂商的soc都有一个默认配置文件,这个默认的配置文件命名格式为:

1
<soc_name>_defconfig

当我们执行一个make <soc_name>_defconfig命令的时候,就会将对应的配置文件也就是<soc_name>_defconfig中的信息导入到源码顶层目录下的.config。配置文件中的语句有两种:

1
2
CONFIG_xxx=y
#CONFIG_xxx is not set
  • CONFIG_xxx=y:表示内核选中了该功能,内核编译时就会将该功能对应的代码编译,内核的体积也会增大。
  • #CONFIG_xxx is not set:表示内核没有选中该功能,内核编译时该功能对应的代码不会被编译,内核的体积也会减小。

2.1.2 修改配置文件

默认配置只能保证内核拥有最基本的功能,我们需要根据自己的实际需求对内核做进一步的配置,修改soc配置文件的话有两种方式,一种是直接修改.config文件或者<soc_name>_defconfig文件,但是这种方式并不推荐,因为我们修改一个配置,这个配置可能相应的依赖另外好几个配置,打开这一个配置,需要打开所有依赖的配置,但是很多时候我们并不能准确的打开所有的依赖配置;方式二就是通过图形界面来修改配置,这种方式可以直接解决依赖问题。

我们通过以下命令打开图形配置界面:

1
make menuconfig

【注意】make menuconfig本质还是修改了.config文件。

点击查看可能出现的问题
  • 问题一
image-20220821210813855

这是因为我们没有安装对应的图形库,我们执行以下命令安装相应的图形库即可:

1
sudo apt-get install libncurses5-dev
  • 问题二
image-20220821211016125

这是因为我们的终端太小了,拉大一点就可以了,上边提示说,必须至少有1980列才可以。

然后我们会看到如下界面:

image-20220821211129157

这样便是正常打开了图形配置界面。

具体的操作说明,在图形界面上都有,这里主要介绍一下配置选项的两种类型:

  • []有两种状态

输入Y,显示*,内核中该功能被选中,相关代码会被编译进内核。

输入N,显示 <space>(就是一个空格),内核中该功能不被选中,相关代码不会被编译进内核。

  • < > 有三种状态

输入Y,显示*,内核中该功能被选中,相关代码会被编译进内核。

输入N,显示 <space>(就是一个空格),内核中该功能不被选中,相关代码不会被编译进内核。

输入M,显示M,内核中该功能被选为模块(被编译为独立的模块)。

【注意】我们可以通过<space>(即空格按键来更改选项的状态)。

2.2 指定使用的处理器

uboot一样,在编译Linux内核之前要先配置Linux内核。每个板子都有其对应的默认配置文件,这些默认配置文件保存在 arch/arm/configs目录中。

导入当前处理器的默认配置的make命令格式为:

1
make <soc_name>_defconfig

例如,我们现在导入exynos_defconfig

1
make exynos_defconfig

然后便会有以下提示信息:

image-20220821144829754

额,并未做任何修改的linux在配置的时候不知道这里为啥有一个警告,不过似乎对后边并没有什么影响。

2.3 编译内核

接下来我们就可以编译linux内核了,我们在终端执行以下命令:

1
make uImage -j4 # 4 核同时进行编译,会快那么一点点

【注意】这个命令用于编译内核(编译选为*的选项到内核)。

点击查看可能出现的问题

要是第一次在ubuntu中编译linux内核的话,不出意外应该会报以下错误:

image-20220821145235696

这里会提示少一个mkimage命令,该命令可在uboot源码中u-boot-2013.01/tools/目录下获取(注意必须是编译后的uboot)。

  • 我们将这个工具拷贝到我们的ubuntu
1
sudo cp ~/5linux/u-boot-2013.01/tools/mkimage /usr/bin/
  • 添加可执行权限
1
sudo chmod 777 /usr/bin/mkimage

然后回到linux源码的顶层目录下重新编译即可。

要是编译成功的话会有以下信息:

image-20220821145924539

2.4 编译内核模块

我们在终端执行以下命令:

1
make modules -j4

【注意】这个命令用于编译内核模块((编译选为M的选项为独立模块)。

例如,我们将DM9000 support配置更改为M状态,该选项位于:

1
2
3
4
Device Drivers  --->
[*] Network device support --->
[*] Ethernet driver support --->
<M> DM9000 support

我们将其选为M状态,然后执行上边的内核模块编译命令,然后我们进入下边的目录下:

1
drivers/net/ethernet/davicom

会发现生成了dm9000.ko文件,这个文件就是kernel object文件(内核模块),该文件作用是把内核的部分功能移动到内核外边,需要的时候插入内核,不需要时卸载。

3. 设备树编译

3.1 添加设备树源码文件

在我们下载的linux内核源码中并没有我们使用的fs4412开发板平台的设备树文件,这里我们从源码支持的平台中找一个硬
件与这个开发板最类似的,在其基础上进行修改,这里我们参考的是samsung公司的origen,拷贝origen的设备树并将其重命名。

1
cp arch/arm/boot/dts/exynos4412-origen.dts arch/arm/boot/dts/exynos4412-fs4412.dts

3.2 修改相应Makefile

  • arch/arm/boot/dts/Makefile

修改相应的makefile文件,使我们添加的设备树源码文件可以参与编译。我们打开相应的Makefile文件,然后找到以下语句:

1
exynos4412-origen.dtb \

在该语句后添加如下内容:

1
exynos4412-fs4412.dtb \

3.3 编译设备树

我们回到linux内核源码顶层目录下,执行以下命令:

1
make dtbs -j4

【注意】该命令会将设备树源文件dts编译为二进制文件dtb

显示如下信息表示编译成功,即在arch/arm/boot/dts/目录下生成了exynos4412-fs4412.dtb

image-20220821151028001

4. 内核启动测试

上边我们已经编译好了内核和设备树,下边我们就测试一下我们自己编译的内核能否启动。之前的时候在学习linux内核加载的时候,我们在ubuntu~/3tftp/fs4412~/4nfs中已经放入了相关的所有文件:

image-20220821151631835
  • 拷贝相关文件

我们将我们自己编译得到的uImage文件和exynos4412-fs4412.dtb拷贝到~/3tftp/fs4412中,根文件系统的话还是先使用之前的。

1
2
cp arch/arm/boot/uImage ~/3tftp/fs4412/
cp arch/arm/boot/dts/exynos4412-fs4412.dtb ~/3tftp/fs4412/
  • 设置uboot变量

这一步我们之前应该设置过了,我们从eMMC启动uboot,从tftp下载linux内核和设备树文件,从nfs挂载根文件系统,相应的两个参数设置如下:

1
2
3
4
fs4412 # print bootcmd
bootcmd=tftp 0x41000000 /fs4412/uImage;tftp 0x42000000 /fs4412/exynos4412-fs4412.dtb;bootm 0x41000000 - 0x42000000
fs4412 # print bootargs
bootargs=root=/dev/nfs nfsroot=192.168.10.101:/home/hk/4nfs/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.10.102
  • 重启相关服务

我们重启一下tftpnfs服务,在ubuntu终端执行:

1
2
sudo service tftpd-hpa restart
sudo service nfs-kernel-server restart
  • 重启开发板

最后就是重启开发板,观察现象了。这样的话其实我们会发现内核可以正常启动了,但是到后边就崩溃卡死了:

image-20220821152715000

原因在于我们在该实验中什么都没有配置,而其他功能都保持默认选项,内核默认配置中没选配我们使用的网卡驱动、nfs 等功能,所以在挂载根文件系统时导致内核崩溃,所以后续我们还需要配置网卡驱动、nfs 等。不过这一部分的主要目的就是实现内核的启动,能出现这些提示已经表明我们的目的达到了。

三、基本驱动移植

1. 串口驱动

串口驱动的移植较为简单,我们只需要在linux内核配置图形界面打开相应的配置即可。

1
2
System Type  --->
(2) S3C UART to use for low-level messages

我们将将System Type菜单下的S3C UART to use for low-level messages修改为2(即使用UART2),初始的时候是3

2. 网卡驱动

2.1 修改内核配置

我们执行以下命令打开图形配置界面:

1
make menuconfig
  • 给内核选配DM9000 网卡驱动,然后选择Save保存
1
2
3
4
Device Drivers  --->
[*] Network device support --->
[*] Ethernet driver support --->
<*> DM9000 support
  • 选配TCP 相关的网络协议(部分功能默认已经选配),然后选择Save保存

因为内核要使用NFS去挂载根文件系统,而NFS是基于TCP 协议实现的,所以这里需要配置一下TCP相关网络协议:

1
2
3
4
5
6
[*] Networking support  ---> 
Networking options --->
<*> Packet socket
<*> Unix domain sockets
[*] TCP/IP networking
[*] IP: kernel level autoconfiguration
  • 选配NFS客户端及相关功能

因为内核要使用NFS去挂载根文件系统,所以需要给内核选配NFS客户端的相关功能:

1
2
3
4
5
6
7
 File systems  ---> 
[*] Network File Systems --->
<*> NFS client support
<*> NFS client support for NFS version 2 (NEW)
<*> NFS client support for NFS version 3 (NEW)
[*] NFS client support for the NFSv3 ACL protocol extension
[*] Root file system on NFS

设置完成后通过方向键选择Save保存即可,然后选择Exit退出该配置界面。

2.2 修改设备树

  • arch/arm/boot/dts/exynos4412-fs4412.dts

接下来我们需要在设备树中添加网卡的硬件信息,我们打开文件exynos4412-fs4412.dts,在文件的末尾处最后一个花括号之前添加以下内容(即要写在根节点之内):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
srom-cs1@5000000 {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x5000000 0x1000000>;
ranges;
ethernet@5000000 {
compatible = "davicom,dm9000";
reg = <0x5000000 0x2 0x5000004 0x2>;
interrupt-parent = <&gpx0>;
interrupts = <6 4>;
davicom,no-eeprom;
mac-address = [00 0a 2d a6 55 a2];
};
};

2.3 修改时钟配置

  • drivers/clk/clk.c

我们需要忽略一些无关的时钟,找到以下语句:

1
static bool clk_ignore_unused;

将其修改为以下内容:

1
static bool clk_ignore_unused=true;

2.4 修改eMMC配置

  • drivers/mmc/core/mmc.c

我们需要找到以下语句:

1
2
3
4
5
6
if (card->ext_csd.rev > 7) {
pr_err("%s: unrecognised EXT_CSD revision %d\n",
mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
goto out;
}

将其修改为以下内容:

1
2
3
4
5
6
7
8
9
10
#if 0
if (card->ext_csd.rev > 7){
#else
if (card->ext_csd.rev > 8){
#endif
pr_err("%s: unrecognised EXT_CSD revision %d\n",
mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL;
goto out;
}

3. 编译内核和设备树

上边我们已经移植好了串口和网卡驱动,然后就是重新编译内核和设备树啦,我们在终端执行以下命令:

1
2
make uImage -j4
make dtbs -j4

4. 内核启动测试

  • 拷贝相关文件

我们将我们自己编译得到的uImage文件和exynos4412-fs4412.dtb拷贝到~/3tftp/fs4412中,根文件系统的话还是先使用之前的。

1
2
cp arch/arm/boot/uImage ~/3tftp/fs4412/
cp arch/arm/boot/dts/exynos4412-fs4412.dtb ~/3tftp/fs4412/
  • 设置uboot变量

这一步我们之前应该设置过了,我们从eMMC启动uboot,从tftp下载linux内核和设备树文件,从nfs挂载根文件系统,相应的两个参数设置如下:

1
2
3
4
fs4412 # print bootcmd
bootcmd=tftp 0x41000000 /fs4412/uImage;tftp 0x42000000 /fs4412/exynos4412-fs4412.dtb;bootm 0x41000000 - 0x42000000
fs4412 # print bootargs
bootargs=root=/dev/nfs nfsroot=192.168.10.101:/home/hk/4nfs/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.10.102
  • 重启相关服务

我们重启一下tftpnfs服务,在ubuntu终端执行:

1
2
sudo service tftpd-hpa restart
sudo service nfs-kernel-server restart
  • 重启开发板

最后就是重启开发板,观察现象了,若成功挂载文件系统,则说明我们已经成功配置了网卡。

image-20220822120944803

四、根文件系统移植

根文件系统移植我们主要是使用Busybox来进行根文件系统的制作,前边我们已经准备好了相关的文件并解压了,我们进入Busybox源码的顶层目录:

1
cd ~/6temp/busybox-1.22.1/

1. 配置busybox

busybox具有和linux内核类似的图形配置界面,我们可以通过下边的命令进入图形配置界面:

1
make menuconfig

配置界面如下:

image-20220822143645370

我们在进行根文件系统制作的时候需要先配置一下busybox,配置如下信息即可:

1
2
3
4
5
Busybox Settings  --->
Build Options --->
[*] Build BusyBox as a static binary (no shared libs)
[ ] Build with Large File Support (for accessing files > 2 GB)
(arm-none-linux-gnueabi-) Cross Compiler prefix

配置完成后通过方向键选择Exit退出,并保存配置信息。

2. 编译busybox

上一步配置完成就可以开始编译busybox了,我们可以执行如下命令:

1
make

这个比较简单,只需要一个make命令就可以开始编译了,编译完成后,会在源码的顶层目录下生成一个busybox文件。

3. 安装busybox

上一步只是进行了编译,我们还需要安装busybox,我们在终端执行:

1
2
3
make install
# 其实我们也可以直接指定安装的目录,避免后边拷贝相关文件的麻烦
make install CONFIG_PREFIX=/home/hk/4nfs/rootfs

安装完成后会有如下提示信息:

image-20220822144449480

若未指定安装的目录,我们会发现在busybox源码顶层目录下会生成一个_install目录,我们看一下这个目录中都有什么:

1
ls _install -ah

我们会发现有如下文件:

image-20220822144724519

这里边就是我们所需要的根文件系统,包含了根文件系统中所需的shell命令文件。

4. 添加lib

4.1 拷贝lib

我们还需要将交叉编译工具链中的库文件拷贝到_install目录下:

1
cp -a  ~/2software/gcc-4.6.4/arm-arm1176jzfssf-linux-gnueabi/lib/ ./_install/

【注意】cp命令要加上-a选项,这个选项通常在复制目录时使用,它保留链接、文件属性,并复制目录下的所有内容。因为我们要靠拷贝的文件中有几个比较特殊的库文件,例如 ld-linux.so.3,这些库文件是符号链接,直接拷贝的话会丢失属性,所以这里要加上-a选项。

4.2 删除lib静态库

我们执行以下命令删除lib中的静态库:

1
sudo rm ./_install/lib/*.a

4.3 删除共享库中符号表

我们还需要删除剩下的共享库中的符号表,该操作需要在root用户中才能操作,这里需要注意我们使用的命令strip也需要是交叉编译器中的命令,而这个命令在root用户下需要带上绝对路径:

1
2
3
sudo su # 切换到root用户
/home/hk/2software/gcc-4.6.4/bin/arm-none-linux-gnueabi-strip _install/lib/*
exit # 退出root用户

我们可以使用以下命令查看”瘦身”前后该目录大小的变化:

1
du -mh _install/lib

“瘦身”完成后没我们可以通过下边的命令来查看是否已经”瘦身”完毕,我们随便查看一个文件:

1
file _install/lib/ld-2.17.so

然后我们会得到如下提示信息:

1
_install/lib/ld-2.17.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, stripped

stripped就表明该库已经删除了符号表,已经完成了”瘦身”。

5. 创建其他目录

在根文件系统中还需要创建其他文件夹,如devprocmntsystmproot等:

1
2
cd _install/
mkdir dev mnt proc root sys tmp var

创建完成后,我们的根文件系统目录如下图所示:

image-20220822151946505

我们刚才创建的几个都是空目录,其实可以发现我们还是少了一个etc目录,我们这里先不创建,一会先进行测试看看没有这个目录会有什么事情发生。

6. 根文件系统初步测试

我们到安装了busybox的目录中,方便后续操作:

1
cd ~/6temp/busybox-1.22.1/_install/
  • 删除原来的根文件系统
1
sudo rm -rf ~/4nfs/rootfs/*
  • 拷贝我们自己制作的根文件系统到相应的挂载目录
1
sudo cp -rf ./* ~/4nfs/rootfs/
  • 重启开发板

注意我们使用的uboot参数与上边linux内核移植时一样,从tftp加载linux内核和设备树,从nfs挂载根文件系统。

image-20220822152625842

从图中我们会发现,根文件系统正常挂载了,并且ls命令也可正常运行,但是有一个错误提示:

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

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

7. 完善根文件系统

7.1 etc目录介绍

7.1.1 目录结构

刚才的错误提示就是提示我们缺少/etc/init.d/rcS文件,但是我们打开以前已经制作好的根文件目录rootfs看一下etc目录下都有哪些文件(前边准备文件的时候有解压过这个目录):

1
2
cd  ~/6temp/rootfs/etc/  # 进入 etc 目录
tree # 查看该目录的结构

然后我们会得到如下信息:

image-20220822153229722

7.1.2 rcS文件

rcS是个shell脚本,Linux内核启动以后需要启动一些服务,而rcS就是规定启动哪些文件的脚本文件。该文件应改位于etc/init.d/rcS文件,我们打开这个文件看一下都有什么:

1
vim ~/6temp/rootfs/etc/init.d/rcS

我们会看到如下信息:

1
2
3
4
#!/bin/sh 
/bin/mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
/sbin/mdev -s
  • 1行:表示这是一个shell脚本。
  • 2行:使用mount命令来挂载所有的文件系统,这些文件系统由文件etc/fstab来指定。
  • 3行和第4行:使用mdev来管理热插拔设备,通过这两行,Linux内核就可以在/dev目录下自动创建设备节点。关于mdev的详细内容可以参考 busybox中的docs/mdev.txt文档。

【说明】

(1)这只是我们自己做的根文件系统中的rcS文件,这个是比较简单的,只有这么几行,但是Ubuntu或者其他大型Linux操作系统中的rcS文件,会更加的复杂,一般来说复杂的rcS文件也是借助其他工具创建的,比如 buildroot等。

(2)etc/init.d/rcS这个文件是需要有可执行文件才可以运行的。

7.1.3 fstab文件

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

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

我们可以打开一个创建好的文件看一下:

1
vim ~/6temp/rootfs/etc/fstab

我们会看到如下内容:

1
2
3
4
5
#device mount-point type options dump fsck orde
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0

7.1.4 inittab文件

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

1
<id>:<runlevels>:<action>:<process>
  • id:每个指令的标识符,不能重复。但是对于busyboxinit来说,id有着特殊意义。对于busybox而言id用来指定启动进程的控制tty,一般我们将串口或者LCD屏幕设置为控制tty
  • runlevels:对busybox来说此项完全没用,所以可以空着。
  • action:动作,用于指定process可能用到的动作。
点击查看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:具体的动作,比如程序、脚本或命令等。

例如,我们打开一个创建好了的文件看一下:

1
vim ~/6temp/rootfs/etc/inittab

我们会看到以下信息:

1
2
3
4
::sysinit:/etc/init.d/rcS 
::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
  • 1行,系统启动以后运行/etc/init.d/rcS这个脚本文件。
  • 2行,系统启动以后,在运行process之前在控制台上显示提示信息,只有用户按下Enter键以后才会执行process
  • 3行,系统启动以后,重启的话运行/sbin/init
  • 4行,按下ctrl+alt+del组合键的话就运行/sbin/reboot,也就是说ctrl+alt+del组合键可以用于重启系统。

7.2 etc目录创建

我们可以自己按照创建好的etc目录文件内容逐个创建文件,也可以拷贝已经做好的etc目录,后续再修改相关文件,以达到自己的预期效果。

  • 这里我们拷贝已经创建好的etc目录:
1
cp -r ~/6temp/rootfs/etc ~/6temp/busybox-1.22.1/_install/
  • rcS文件添加可执行权限
1
sudo chmod +x ~/6temp/busybox-1.22.1/_install/etc/init.d/rcS

7.3 根文件系统测试

  • 删除原来的根文件系统
1
sudo rm -rf ~/4nfs/rootfs/*
  • 拷贝我们自己制作的根文件系统到相应的挂载目录
1
sudo cp -rf ~/6temp/busybox-1.22.1/_install/* ~/4nfs/rootfs/
  • 重启开发板

这一次我们会发现启动后没有错误提示了:

image-20220822161009551

8. busybox中文字符支持

我们是通过nfs挂载的根文件系统,我们可以在ubuntu的相关目录下创建一个中文命名的目录,例如:

1
2
3
cd ~/4nfs/rootfs/
mkdir 桌面
ls

然后我们会看到ubuntu下目录详情如下:

1
bin  dev  etc  lib  linuxrc  mnt  proc  root  sbin  sys  tmp  usr  var  桌面

然而我们到SecureCRT中,看一下根目录:

1
2
3
[root@farsight]#ls
bin etc linuxrc proc sbin tmp var
dev lib mnt root sys usr ??

我们会发现并不支持中文而中中文目录显示为?。老版本的busybox是支持中文的,但是不知道从哪个版本开始busybox中的shell命令对中文输入即显示做了限制,即使内核支持中文但在shell下也依然无法正确显示。这个时候就需要修改busybox源码了。

我们先进入busybox源码顶层目录:

1
cd ~/6temp/busybox-1.22.1/

8.1 printable_string.c文件修改

首先我们需要修改libbb/printable_string.c文件,我们打开该文件后,会发现这里边只有一个printable_string函数,该函数中有这么几条语句:

1
2
3
4
5
6
/* 下边if出现在34行 */
if (c >= 0x7f)
break;
/* 下边if出现在48行 */
if (c < ' ' || c >= 0x7f)
*d = '?';

通过这几个语句我们知道,当字符大于0X7F以后就跳出去了。如果支持UNICODE码的话,当字符大于0X7F就直接输出?。我们将上边的两个if语句进行修改:

1
2
3
4
5
6
7
/* 下边if出现在34行(将这个if注释掉) */
// if (c >= 0x7f)
// break;
/* 下边if出现在48行 */
// if (c < ' ' || c >= 0x7f)
if (c < ' ')
*d = '?';

这两处修改主要是为了禁止字符大于0X7F以后break和输出?

8.2 unicode.c文件修改

我们打开libbb/unicode.c文件,然后找到unicode_conv_to_printable2函数,在该函数中有这么几行:

1
2
3
4
5
/* 下边的这个三目运算符语句出现在 1022 行*/
*d++ = (c >= ' ' && c < 0x7f) ? c : '?';
/* 下边的这个if 出现在 1030 行*/
if (c < ' ' || c >= 0x7f)
*d = '?';

1022行表示当字符大于0X7F以后,*d++就为?1030行和1031行表示,当字符大于0X7F以后,*d也为?。我们将其修改为以下内容:

1
2
3
4
5
6
7
/* 下边的这个三目运算符语句出现在 1022 行*/
// *d++ = (c >= ' ' && c < 0x7f) ? c : '?';
*d++ = (c >= ' ') ? c : '?';
/* 下边的这个if 出现在 1030 行*/
// if (c < ' ' || c >= 0x7f)
if (c < ' ')
*d = '?';

8.3 busybox配置

我们执行以下命令打开busybox的图形配置界面:

1
make menuconfig

然后我们需要使能busyboxunicode编码以支持中文,配置路径如下:

1
2
3
4
Busybox Settings  --->
General Configuration --->
[*] Support Unicode
[*] Check $LC_ALL, $LC_CTYPE and $LANG environment variables

然后退出,一定要记得保存。

8.4 重新制作根文件系统

按照前边的步骤重新制作根文件系统即可。

8.5 中文支持测试

image-20220822165230442