正点原子嵌入式linux驱动开发——Busybox根文件系统构建

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

前面已经移植了TF-A、Uboot和Linux kernel就剩最后一个 rootfs(根文件系统)了本章就来学习一下根文件系统的组成以及如何构建根文件系统。这是Linux系统移植的最后一步根文件系统构建好以后就意味着拥有了一个完整的、可以运行的最小系统 。以后就在这个最小系统上编写、测试Linux驱动移植一些第三方组件逐步的完善这个最小系统。最终得到一个功能完善、驱动齐全、相对完善的操作系统。

根文件系统简介

根文件系统一般也叫做rootfs那么什么叫根文件系统看到“文件系统”这四个字一般就是FATFS、FAT、EXT4、YAFFS和NTFS等这样的文件系统。在这里根文件系统并不是以上这些文件系统代码在学单片机的时候听到的FATFS是具体的文件系统代码这个是属于Linux内核的一部分。Linux中的根文件系统更像是一个文件夹或者叫做目录在这个目录里面会有很多的子目录。根目录和子目录中会有很多的文件这些文件是Linux运行所必须的比如库、常用的软件和命令、设备文件、配置文件等等。以后说到文件系统如果不特别指明统一表示根文件系统。对于根文件系统专业的解释百度百科上是这么说的

根文件系统首先是内核启动时所mount(挂载)的第一个文件系统内核代码映像文件保存在根文件系统中 而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。

百度百科上说内核代码镜像文件保存在根文件系统中但是嵌入式Linux并没有将内核代码镜像保存在根文件系统中而是保存到了其他地方。比如NAND Flash的指定存储地址、EMMC专用分区中。根文件系统是Linux内核启动以后挂载(mount)的第一个文件系统然后从根文件系统中读取初始化脚本比如rcS、inittab等。根文件系统和Linux内核是分开的单独的Linux内核是没法正常工作的必须要搭配根文件系统。如果不提供根文件系统Linux内核在启动的时候就会提示内核崩溃(Kernel panic)的提示这个在之前的笔记中已经有看到过了。

根文件系统的这个“根”字就说明了这个文件系统的重要性它是其他文件系统的根没有这个“根”其他的文件系统或者软件就不能工作。比如用的ls、mv、ifconfig等命令其实就是一个个小软件只是这些软件没有图形界面而且需要通过输入命令的形式来运行。这些小软件就保存在根文件系统中这些小软件是怎么来的呢这个就是本章教程的目的构建自己的根文件系统 这个根文件系统是满足Linux运行的最小根文件系统后续可以根据自己的实际工作需求不断的去填充这个最小根文件系统最终使其成为一个相对完善的根文件系统

在构建根文件系统之前先来看一下根文件系统里面大概都有些什么内容以Ubuntu为例根文件系统的目录名字为‘ ‘/’可以直接cd进去进入后输入“ls”查看根目录下的内容结果如图所示
Ubuntu根目录
接下来看一下常用的子目录。

/bin目录

看到“bin”应该能想到bin文件bin文件就是可执行文件。所以此目录下存放着系统需要的可执行文件一般都是一些命令比如ls、mv等命令。此目录下的命令所有的客户都可以使用。

/dev目录

dev是device的缩写所以此目录下的文件都是和设备有关的此目录下的文件都是设备文件。在Linux下一切皆文件即使是硬件设备也是以文件的形式存在的比如/dev/ttySTM0(STM32MP1根目录会有此文件)就表示STM32MP157的串口4要想通过串口4发送或者接收数据就要操作文件/dev/ttySTM0通过对文件/dev/ttySTM0的读写操作来实现串口4的数据收发。

/etc目录

此目录下存放着各种配置文件可以进入Ubuntu的/etc目录看一下里面的配置文件非常多但是在嵌入式Linux下此目录会很简洁。

/lib目录

lib是library的简称也就是库的意思因此此目录下存放着Linux所必须的库文件。这些库文件是共享库命令和用户编写的应用程序要使用这些库文件。

/mnt目录

临时挂载目录一般是空目录可以在此目录下创建空的子目录比如/mnt/sd、/mnt/usb这样就可以将SD卡或者U盘挂载到/mnt/sd或者/mnt/usb目录中。

/proc目录

此目录一般是空的当Linux系统启动以后会将此目录作为proc文件系统的挂载点proc是个虚拟文件系统没有实际的存储设备。proc里面的文件都是临时存在的 一般用来存储系统运行信息文件。

/usr目录

要注意usr不是user的缩写而是Unix Software Resource的缩写也就是Unix操作系统软件资源目录。Linux一般被成为类Unix操作系统苹果的MacOS也是类Unix操作系统。既然是软件资源目录因此/usr目录下也存放着很多软件一般系统安装完成以后此目录占用的空间最多

/var目录

此目录存放一些可以改变的数据。

/sbin目录

此目录也用于存放一些可执行文件但是此目录下的文件或者说命令只有管理员才能使用主要用户系统管理

/sys目录

系统启动以后此目录作为sysfs文件系统的挂载点sysfs是一个类似于proc文件系统的特殊文件系统sysfs也是基于ram的文件系统也就是说它也没有实际的存储设备。此目录是系统设备管理的重要目录此目录通过一定的组织结构向用户提供详细的内核数据结构信息

/opt

可选的文件、软件存放区由用户选择将哪些文件或软件放到此目录中。

关于Linux的根目录就介绍到这里接下来的构建根文件系统就是研究如何创建上面这些子目录以及子目录中的文件

BusyBox构建根文件系统

BusyBox简介

上一小节说了根文件系统里面就是一堆的可执行文件和其他文件组成的那么其实需要一种工具负责“收集”这些文件然后将其打包给开发者可以直接拿来用。答案是有的它就叫做BusyBox其名字分为“Busy”和“Box”也就是忙碌的盒子。盒子是用来放东西的忙碌的是因为它要提供根文件系统所需的文件所以忙碌。 BusyBox是一个集成了大量的Linux命令和工具的软件像ls、mv、ifconfig等命令BusyBox都会提供。 BusyBox就是一个大的工具箱这个工具箱里面集成了Linux的许多工具和命令。一般下载 BusyBox的源码然后配置 BusyBox选择自己想要的功能最后编译即可

BusyBox可以在其官网下载到官网地址为BusuBox官网官网比较简陋如下图所示
BusyBox官网
在官网左侧的“Get BusyBox”栏有一行“Download Source”点击“Download Source”即可打开BusyBox的下载页如下图所示
BusyBox下载页
从上图可以看出正点原子教程写出来的时候最新的BusyBox版本是1.32.0下载busybox-1.32.0.tar.bz2这个压缩包即可正点原子是提供的并且将其放到了开发板光盘中就是busybox-1.32.0.tar.bz2。BusyBox准备好以后就可以构建根文件系统了。

编译BusyBox构建根文件系统

一般在Linux驱动开发的时候都是通过nfs挂载根文件系统的当产品最终上市开卖的时候才会将根文件系统烧写到EMMC或者NAND中。所以要在 之前设置的nfs服务器目录中创建一个名为rootfs的子目录使用如下命令创建名为rootfs的子目录

cd /home/zuozhongkai/linux/nfs
mkdir rootfs

创建好的rootfs子目录就用来存放根文件系统了。

将busybox-1.32.0.tar.bz2发送到Ubuntu中存放位置随便选择。然后使用如下命令将其解压

tar -vxjf busybox-1.32.0.tar.bz2

解压完成以后进入到busybox-1.32.0目录中此目录中的文件和文件夹如下图所示
busybox-1.32.0目录内容

修改Makefile添加编译器

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

示例代码18.2.2.1 Makefile代码段 
164 CROSS_COMPILE ?= /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf- 
...... 
190 ARCH ?= arm

上述示例代码中CROSS_COMPILE使用了绝对路径来防止编译出错。

busybox中文字符支持

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

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

示例代码18.2.2.2 libbb/printable_string.c代码段 
12 const char* FAST_FUNC printable_string2(uni_stat_t *stats, const char *str) 
13 { 
14 char *dst; 
15 const char *s; 
16 
17 s = str; 
18 while (1) { 
19     unsigned char c = *s; 
20     if (c == '\0') { 
21         /* 99+% of inputs do not need conversion */ 
22         if (stats) { 
23             stats->byte_count = (s - str); 
24             stats->unicode_count = (s - str); 
25             stats->unicode_width = (s - str); 
26         } 
27         return str; 
28     } 
29     if (c < ' ') 
30         break; 
31     if (c >= 0x7f) 
32         break; 
33     s++; 
34 } 
35
36 #if ENABLE_UNICODE_SUPPORT 
37 dst = unicode_conv_to_printable(stats, str); 
38 #else 
39 { 
40     char *d = dst = xstrdup(str); 
41     while (1) { 
42         unsigned char c = *d; 
43         if (c == '\0') 
44             break; 
45         if (c < ' ' || c >= 0x7f) 
46             *d = '?'; 
47         d++; 
48     } 
49     if (stats) { 
50         stats->byte_count = (d - dst); 
51         stats->unicode_count = (d - dst); 
52         stats->unicode_width = (d - dst); 
53     } 
54 } 
55 #endif 
56 return auto_string(dst); 
57 }

第31和32行当字符大于0X7F以后就跳出去了。

第45和46行如果支持UNICODE码的话当字符大于0X7F就直接输出‘?’。

所以需要对这 4行代码进行修改修改以后如下所示

示例代码18.2.2.3 libbb/printable_string.c代码段 
12 const char* FAST_FUNC printable_string2(uni_stat_t *stats, const char *str) 
13 { 
14 char *dst; 
15 const char *s; 
16 
17 s = str; 
18 while (1) { 
19     unsigned char c = *s; 
20     if (c == '\0') { 
21         /* 99+% of inputs do not need conversion */ 
22         if (stats) { 
23             stats->byte_count = (s - str); 
24             stats->unicode_count = (s - str); 
25             stats->unicode_width = (s - str); 
26         } 
27         return str; 
28     } 
29     if (c < ' ') 
30         break; 
31     /* 注释掉下面这个两行代码 */ 
32     /* if (c >= 0x7f) 
33         break; */
34     s++; 
35 } 
36
37 #if ENABLE_UNICODE_SUPPORT 
38 dst = unicode_conv_to_printable(stats, str); 
39 #else 
40 { 
41     char *d = dst = xstrdup(str); 
42     while (1) { 
43         unsigned char c = *d; 
44         if (c == '\0') 
45             break; 
46         /* 修改下面代码 */ 
47         /* if (c < ' ' || c >= 0x7f) */ 
48         if( c < ' ') 
49             *d = '?'; 
50         d++;
51     } 
52     if (stats) { 
53         stats->byte_count = (d - dst); 
54         stats->unicode_count = (d - dst); 
55         stats->unicode_width = (d - dst); 
56     } 
57 } 
58 #endif 
59 return auto_string(dst); 
60 }

示例代码18.2.2.3中修改的内容主要就是禁止字符大于0X7F以后break和输出‘?’。

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

示例代码18.2.2.4 libbb/unicode.c代码段 
1009 static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags) 
1010 { 
1011     char *dst; 
1012     unsigned dst_len; 
1013     unsigned uni_count;
1014 unsigned uni_width; 
1015 
1016     if (unicode_status != UNICODE_ON) { 
1017         char *d; 
1018         if (flags & UNI_FLAG_PAD) { 
1019             d = dst = xmalloc(width + 1); 
1020             while ((int)--width >= 0) { 
1021                 unsigned char c = *src; 
1022                 if (c == '\0') { 
1023                     do 
1024                         *d++ = ' '; 
1025                     while ((int)--width >= 0); 
1026                     break; 
1027                 } 
1028                 *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; 
1029                 src++; 
1030             } 
1031             *d = '\0'; 
1032         } else { 
1033             d = dst = xstrndup(src, width); 
1034             while (*d) { 
1035                 unsigned char c = *d; 
1036                 if (c < ' ' || c >= 0x7f) 
1037                     *d = '?'; 
1038                 d++; 
1039             } 
1040         } 
1041         if (stats) { 
1042             stats->byte_count = (d - dst); 
1043             stats->unicode_count = (d - dst); 
1044             stats->unicode_width = (d - dst); 
1045         } 
1046         return dst; 
1047     } 
...... 
1139     return dst; 
1140 }

第1028行当字符大于0X7F以后 *d++就为‘?’。

第1036和1037行当字符大于0X7F以后*d也未’?'。

修改示例代码18.2.2.4修改后如下所示

示例代码18.2.2.5 libbb/unicode.c代码段 
1009 static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags) 
1010 { 
1011     char *dst; 
1012     unsigned dst_len; 
1013     unsigned uni_count;
1014 unsigned uni_width; 
1015 
1016     if (unicode_status != UNICODE_ON) { 
1017         char *d; 
1018         if (flags & UNI_FLAG_PAD) { 
1019             d = dst = xmalloc(width + 1); 
1020             while ((int)--width >= 0) { 
1021                 unsigned char c = *src; 
1022                 if (c == '\0') { 
1023                     do 
1024                         *d++ = ' '; 
1025                     while ((int)--width >= 0); 
1026                     break; 
1027                 } 
1028                 /* 修改下面一行代码 */ 
1029                 /* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */ 
1030                 *d++ = (c >= ' ') ? c : '?';
1031                 src++; 
1032             } 
1033             *d = '\0'; 
1034         } else { 
1035             d = dst = xstrndup(src, widh); 
1036             while (*d) { 
1037                 unsigned char c = *d; 
1038                 /* 修改下面一行代码 */ 
1039                 /* if (c < ' ' || c >= 0x7f) */ 
1040                 if(c < ' ') 
1041                     *d = '?'; 
1042                 d++; 
1043             } 
1044         } 
1045         if (stats) { 
1046             stats->byte_count = (d - dst); 
1047             stats->unicode_count = (d - dst); 
1048             stats->unicode_width = (d - dst); 
1049         } 
1050         return dst; 
1051     } 
...... 
1143     return dst; 
1144 }

示例代码18.2.2.5中的修改内容同样主要是禁止字符大于0X7F的时候设置为‘?’。busybox中文字符支持跟代码修改有关的就改好了最后还需要配置busybox来使能unicode码这个稍后配置busybox的时候再设置。

配置busybox

和之前编译Uboot、Linux kernel一样要先对busybox进行默认的配置有以下几种配置选项

  1. defconfig缺省配置也就是默认配置选项。
  2. allyesconfig全选配置也就是选中busybox的所有功能。
  3. allnoconfig最小配置。

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

make defconfig

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

make menuconfig

打开后如下图所示
busybox图形化配置界面
配置路径如下

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

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

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

结果如下图所示
选择“vi-style line editing commands”
继续配置如下路径配置项

Location:
-> Linux Module Utilities
-> Simplified modutils

默认会选中“Simplified modutils”这里要取消勾选结果如下图所示
取消选中“Simplified modutils”
继续配置如下路径配置项

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

结果如下图所示
“mdev”配置项
最后就是使能busybox的unicode编码以支持中文配置路径如下

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

结果如下图所示
中文支持
busybox的配置就到此结束了也可以根据自己的实际需求选择配置其他的选项。

使用图形化修改过Busybox的配置以后最好保存一下配置文件以防“make clean”以后删除掉以前的配置在配置主界面上选中“Save Configuration to an Alternate File”如下图所示
保存配置文件
选中以后会需要输入配置文件名Busybox的默认配置文件都是保存到configs目录下配置文件的名字后缀必须是“_defconfig”这里设置配置文件名字为“stm32mp1_atk_defconfig”如下图所示
设置配置文件名字
点击上图中“Ok”即可完成保存以后要使用自己的配置文件直接输入以下命令

make stm32mp1_atk_defconfig

编译busybox

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

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

COFIG_PREFIX指定编译结果的存放目录比如正点原子的文档中存放到“/home/zuozhongkai/linux/nfs/rootfs”目录中等待编译完成。编译完成以后如下图所示
busybox编译完成
编译完成后会在busybox的工具和文件都会被安装到rootfs目录中。可以看到rootfs目录下有bin、sbin和usr这三个目录以及linuxrc这个文件。前面说过Linux内核init进程最后会查找用户空间的 init程序找到以后就会运行这个用户空间的init程序从而切换到用户态。如果bootargs设置init=/linuxrc那么linuxrc就是可以作为用户空间的 init程序所以用户态空间的init程序是busybox来生成的

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

向根文件系统添加lib库

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

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

mkdir lib

lib文件夹创建好了库文件从哪里来呢lib库文件从交叉编译器中获取前面搭建交叉编译环境的时候将交叉编译器存放到了“/usr/local/arm/”目录中。交叉编译器里面有很多的库文件可以直接把所有的库文件都放到根文件系统中。进入如下路径对应的目录

/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib

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

cp *so* /home/zuozhongkai/linux/nfs/rootfs/lib/ -d

后面的“-d”表示拷贝符号链接这里有个比较特殊的库文件ld-linux-armhf.so.3此库文件也是个符号链接相当于Windows下的快捷方式。会链接到库ld-2.30.so上输入命令“ls ld-linux-armhf.so.3 -l”查看此文件详细信息如图下图所示
文件lid-linux-armhf.so.3
从上图可以看出ld-linux-armhf.so.3后面有个“->”表示其是个软连接文件链接到文件ld-2.30.so因为是一个“快捷方式”因此大小只有10B。但是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-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib目录中重新拷贝ld-linux-armhf.so.3命令如下

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

拷贝完成以后再到rootfs/lib目录下查看ld-linux-armhf.so.3文件详细信息如下图所示
文件ld-linux-armhf.so.3
从上图可以看出此时ld-linux-armhf.so.3已经不是软连接了而是实实在在的一个库文件而且文件大小为1279392B。

继续进入如下目录中

/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/lib

此目录下也有很多的的*so*和.a库文件将其也拷贝到rootfs/lib目录中命令如下

cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/lib/ -d

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

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

在rootfs的usr目录下创建一个名为lib的目录将如下目录中的库文件拷贝到rootfs/usr/lib目录下

/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/usr/lib

将此目录下的so和.a库文件都拷贝到rootfs/usr/lib目录中命令如下

cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -d

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

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

结果如下图所示
lib和/usr/lib大小
可以看出lib和usr/lib这两个文件的大小分别为158MB和89MB加起来就是158+89=247MB还是挺大的但是正点原子STM23MP157开发板板载8GB的EMMC无须担心存储不够。

创建其他文件夹

在根文件系统中创建其他文件夹如dev、proc、mnt、sys、tmp、etc和root等创建完成以后如下图所示
创建后的rootfs
目前看起来已经完成了准备可以测试一下。

根文件系统初步测试

修改Ubuntu的nfs版本配置

测试根文件系统的时候不是直接烧写到EMMC里面这样测试效率太低了Ubuntu的rootfs目录已经保存了根文件系统只需要在开发板上通过nfs挂载Ubuntu下的rootfs目录即可。也就是说根文件系统一直在Ubuntu下开发板通过网络在使用这个根文件系统这样方便开发调试。

但是Ubuntu18的 nfs默认只支持3和4版本的nfsuboot默认使用的是版本2所以直接需要修改Ubuntu18的nfs配置否则nfs根文件系统会报如下图所示错误导致无法挂载。
nfs挂载错误
解决方法很简单打开Ubuntu下的/etc/default/nfs-kernel-server文件然后在最后面添加下面这一行

RPCNFSDOPTS="--nfs-version 2,3,4 --debug --syslog"

添加完成以后保存退出输入如下命令重启NFS服务即可

sudo /etc/init.d/nfs-kernel-server restart

bootargs环境变量设置

接下来要设置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地址。

<root-dir>根文件系统的存放路径教程中就是/home/zuozhongkai/linux/nfs/rootfs。

<nfs-options>NFS的其他可选选项一般不设置。

<client-ip>客户端IP地址也就是开发板的IP地址 Linux内核启动以后就会使用此IP地址来配置开发板。此地址一定要和Ubuntu主机在同一个网段内并且没有被其他的设备使用在Ubuntu中使用ping命令ping一下就知道要设置的IP地址有没有被使用如果不能ping通就说明没有被使用那么就可以设置为开发板的IP地址。

<server-ip>服务器IP地址前面已经说了。

<gw-ip>网关地址教程中就是192.168.1.1。

<netmask>子网掩码教程中就是255.255.255.0。

<hostname>客户机的名字一般不设置此值可以空着。

<device>设备名也就是网卡名一般是eth0eth1….正点原子STM32MP157开发板
只有一个网口名字为eth0。

<autoconf>自动配置一般不使用所以设置为off。

<dns0-ip>DNS0服务器IP地址不使用。

<dns1-ip>DNS1服务器IP地址不使用。

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

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

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

setenv bootargs 'console=ttySTM0,115200 root=/dev/nfs nfsroot=192.168.1.249:/home/zuozhongkai/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.250:192.168.1.249:192.168.1.1:255.255.255.0::eth0:off' //设置bootargs
saveenv //保存环境变量

设置完成后使用“boot”命令启动Linux内核结果如下图所示
进入根文件系统
注意由于是通过网络挂载根文件系统因此Linux系统在启动的时候需要初始化网络因此会卡一下大概在初始化完EMMC以后会卡几秒 钟这是正常现象。

可以输入“ls”测试一下如下图所示
ls命令测试
可以看出 ls命令工作正常那么是不是说明rootfs就制作成功了呢注意在进入根文件系统的时候会有下面这一行错误提示

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

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

无法进入根文件系统

如果使用nfs挂载根文件系统的时候发现并没有进入根文件系统那么可能的原因有一下几点。

1、网络硬件问题

可能是开发板网络硬件有问题首先检查一下网线是否接好网络硬件是否有损坏。最简单的检测方法就是烧写正点原子出厂系统然后在出厂系统里面测试网络是否能正常工作如果可以正常工作的话就说明不是硬件问题。

2、bootargs环境变量设置错误

bootargs环境变量设置错误是最常见的典型的就是单词写错少个字母或者多个字母直接进入uboot里面输入

print bootargs

输入上述命令以后就会打印出bootargs环境变量的值如下图所示
bootargs环境变量值

3、bootargs环境变量没有传递给内核

这个问题不常见但却是最容易忽略的bootargs环境变量的值会传递给内核作为命令行(command line)参数Linux内核启动的时候会打印出命令行参数如下图所示
命令行参数
从上图可以看出有“Kernel command line:”这一行这一行就是命令行参数可以看到命令行参数和bootargs环境变量一模一样如果不一致的话就说明 bootargs没有传递给内核。

没有传递进来的原因无非两点

  1. bootargs环境变量设置错误比如设置的时候将bootargs打错导致无法正确识别为“bootargs”。
  2. bootargs环境变量被覆写也就是说bootargs环境变量设置正确但是就是传递不进来或者传递给命令行参数是错误的这是因为uboot在启动linux内核的时候将bootargs覆写了。比如ST官方uboot里面的bootcmd是一个脚本文件此脚本文件比较复杂会经过一系列的计算得到具体的bootcmd命令以及bootargs参数在这个过程中bootargs环境变量就会被覆写掉解决方法就是在uboot命令行先输入“eraseenv”命令来擦除默认环境变量内容然后再手动设置bootcmd和bootargs这两个环境变量的值其他的网络IP地址、MAC地址能也需要重新设置。

完善根文件系统

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

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

示例代码18.4.1.1 /etc/init.d/rcS文件 
1 #!/bin/sh 
2 
3 PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH 
4 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib 
5 export PATH LD_LIBRARY_PATH 
6 
7 mount -a 
8 mkdir /dev/pts 
9 mount -t devpts devpts /dev/pts 
10 
11 echo /sbin/mdev > /proc/sys/kernel/hotplug
12 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文档。

示例代码18.4.1.1中的rcS文件内容是最精简的如果去看Ubuntu或者其他大型Linux操作系统中的rcS文件就会发现其非常复杂。因为是初次学习所以不用搞这么复杂的而且这么复杂的rcS文件也是借助其他工具创建的比如buildroot等。

创建好文件/etc/init.d/rcS以后一定要给其可执行权限使用如下命令给予权限

chmod 777 rcS

设置好以后就重新启动Linux内核启动以后如下图所示
Linux启动过程
从上图可以看到提示找不到/etc/fstab文件还有一些其他的错误先把/etc/fstab这个错误解决了。前面说了“mount -a”挂载所有根文件系统的时候需要读取/etc/fstab因为/etc/fstab里面定义了该挂载哪些文件接下来就是创建/etc/fstab文件。

创建/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文件中输入如下内容

示例代码18.4.2.1 /etc/fstab文件 
1 #<file system> <mount point> <type> <options> <dump> <pass>
2 proc /proc proc defaults 0 0 
3 tmpfs /tmp tmpfs defaults 0 0 
4 sysfs /sys sysfs defaults 0 0

fstab文件创建完成后重新启动Linux结果如下图所示
Linux启动过程
从上图可以看出在运行/etc/init.d/rcS脚本第11行出现问题提示不能创建“/proc/sys/kernel/hotplug”这个是Linux内核配置问题最后在讲解怎么处理。接下来还需要创建一个文件/etc/inittab。

创建/etc/inittab文件

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

<id>:<runlevels>:<action>:<process>

<id>每个指令的标识符不能重复。但是对于busybox的init来说 <id>有着特殊意义。对于busybox而言<id>用来指定启动进程的控制tty一般将串口或LCD屏幕设置为控制tty。

<runlevels>对busybox来说此项完全没用所以空着。

<action>动作用于指定<process>可能用到的动作。busybox支持的动作如下图所示
动作
<process>具体的动作比如程序、脚本或命令等。

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

示例代码18.4.3.1 /etc/inittab文件 
1 #etc/inittab 
2 ::sysinit:/etc/init.d/rcS 
3 console::askfirst:-/bin/sh 
4 ::restart:/sbin/init 
5 ::ctrlaltdel:/sbin/reboot 
6 ::shutdown:/bin/umount -a -r
7 ::shutdown:/sbin/swapoff -a

第2行系统启动以后运行/etc/init.d/rcS这个脚本文件。

第3行将console作为控制台终端也就是ttySTM0。

第4行重启的话运行/sbin/init。

第5行按下ctrl+alt+del组合键的话就运行/sbin/reboot看来ctrl+alt+del组合键用于重
启系统。

第6行关机的时候执行/bin/umount也就是卸载各个文件系统。

第7行关机的时候执行/sbin/swapoff也就是关闭交换分区。

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

使能内核uevet helper

根文件系统要创建的文件已经全部完成了但还是会存在如下图所示问题
hotplug报错
前面已经说了这是Linux内核配置问题只要配置一下内核就行了路径如下

Location:
-> Device Drivers
-> Generic Driver Options
->Support for uevent helper //选中

配置如下图所示
内核配置
选中上图中的“Support for uevent helper”选项选中以后就会在.config中存在

CONFIG_UEVENT_HELPER=y

配置好以后重新编译内核然后使用新的内核启动如下图所示
根文件系统运行结果
从上图可以看出没有任何错误提示说明根文件系统工作已经正常了。接下来就要对根文件系统进行其他的测试比如自己编写的软件运行是否正常、是否支持软件开机自启动、中文支持是否正常以及能不能链接等。

根文件系统其他功能测试

软件运行测试

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

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

示例代码18.5.1.1 hello.c文件 
1 #include <stdio.h> 
2 
3 int main(void) 
4 { 
5     while(1) { 
6         printf("hello world!\r\n"); 
7         sleep(2); 
8     } 
9     return 0; 
10 }

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

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

使用arm-none-linux-gnueabihf-gcc将hello.c编译为hello可执行文件。使用“file”命令查看文件类型以及编码格式

file hello //查看 hello的文件类型以及编码格式

结果如下图所示
查看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

结果如下图所示
hello运行结果
可以看出 hello这个软件运行正常说明根文件系统中的共享库是没问题的要想终止hello的运行按下“ctrl+c”组合键即可。可以通过让hello进入后台运行从而正常运行并使得终端可以使用。让一个软件进入后台的方法很简单运行软件的时候加上“&”即可比如“./hello &”就是让hello在后台运行。在后台运行的软件可以使用“kill -9 pid(进程 ID)”命令来关闭掉首先使用“ps”命令查看要关闭的软件PID是多少ps命令用于查看所有当前正在运行的进程并且会给出进程的PID

通过“ps”命令查到hello对应的PID为137可以使用如下命令关闭在后台运行的hello软件

kill -9 137

因为hello在不断的输出“hello world”所以输入看起来会被打断其实是没有的因为是输入而 hello是输出。在数据流上是没有打断的只是显示在SecureCRT上就好像被打断了所以只管输入“kill -9 137”即可。 hello被kill以后会有提示如下图所示
提示hello被kill
再去用ps命令查看一下当前的进程发现没有hello了。这个就是Linux下的软件后台运行以及如何关闭软件的方法重点就是3个操作软件后面加“&”、使用ps查看要关闭的软件PID、使用“kill -9 pid”来关闭指定的软件

中文字符测试

在ubuntu中向在rootfs目录新建一个名为“中文测试”的文件夹然后在MobaXterm下查看中文名能不能显示正确。输入“ls”命令结果如下图所示
中文文件夹测试
可以看出“中文测试”这个文件夹显示正常接着“touch”命令在“中文测试”文件夹中新建一个名为“测试文档 .txt”的文件并且使用vim编辑器在其中输入“这是一个中文测试文件”借此来测试一下中文文件名和中文内容显示是否正 常。在MobaXterm中使用“cat”命令来查看“测试文档 .txt”中的内容结果如下图所示
中文文档内容显示
从上图可以看出“测试文档 .txt”的中文内容显示正确而且中文路径也完全正常说明根文件系统已经完美支持中文了

开机自启动测试

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

示例代码18.5.3.1 /etc/init.d/rcS文件 
1 #!/bin/sh 
2 PATH=/sbin:/bin:/usr/sbin:/usr/bin 
3 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib 
4 runlevel=S
5 umask 022
5 export PATH LD_LIBRARY_PATH 
6 export PATH LD_LIBRARY_PATH runlevel 
7 
8 mount -a 
9 mkdir /dev/pts 
10 mount -t devpts devpts /dev/pts 
11 
12 echo /sbin/mdev > /proc/sys/kernel/hotplug 
13 mdev -s 
14 
15 #开机自启动 
16 cd /drivers 
17 ./hello & 
18 cd /

第16行进入drivers目录因为要启动的软件存放在drivers目录下。

第17行以后台方式执行hello这个软件。

第18行退出drivers目录进入到根目录下。

自启动代码添加完成以后就可以重启开发板看看 hello这个软件会不会自动运行。结果如下图所示
hello开机自启动
从上图可看出hello开机自动运行证明开机自启动成功。

外网连接测试

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

ping www.baidu.com

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

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

示例代码18.5.4.1 resolv.conf文件内容 
1 nameserver 114.114.114.114 
2 nameserver 192.168.1.1

设置很简单nameserver表示这是个域名服务器设置了两个域名服务器地址114.114.114.114和192.168.1.1也可以改为其他的域名服务器试试。如果使用“udhcpc”命令自动获取IP地址“udhcpc”命令会修改nameserver值一般是将其设置为对应的网关地址。修改好以后保存退出重启开发板重启以后重新ping一下百度官网结果如下图所示
ping结果
可以看出ping百度官网成功了域名也成功的解析了至此根文件系统就彻底的制作完成这个根文件系统最好打包保存一下防止以后做实验不小心破坏了根文件系统又得从头制作根文件系统。uboot、Linux kernel、rootfs这三个共同构成了一个完整的Linux系统现在的系统至少是一个可以正常运行的系统后面就可以在这个系统上完成Linux驱动开发的学习

烧写根文件系统到EMMC中

一个最小的根文件系统已经制作好了而且通过nfs测试正常接下来就学习一下如何将其烧写到开发板 EMMC中。根文件系统烧写进去以后一个完成的嵌入式Linux开发环境就准备好了以后实际产品开发也是用的这套流程

根文件系统打包

根文件系统打包和之前Linux内核移植中对uImage和stm32mp157d-atk.dtb打包方法一样也是制
作ext4格式的根文件系统包这里需要对/home/zuozhongkai/linux/nfs/rootfs目录进行打包这里再讲解一遍。

新建ext4格式磁盘

首先新建一个ext4格式的磁盘然后挂载这个ext4格式的磁盘将/home/zuozhongkai/linux/nfs/rootfs这个目录下的文件拷贝到这个ext4磁盘即可。

首选创建一个名为rootfs文件夹注意不要和 /home/zuozhongkai/linux/nfs/rootfs这个目录重
复了比如教程中就在/home/zuozhongkai/linux/目录下创建一个名为“rootfs”目录然后输入如下命令创建ext4磁盘

示例代码18.6.1.1 ext4磁盘创建命令 
1 cd rootfs 
2 dd if=/dev/zero of=rootfs.ext4 bs=1M count=1024 
3 mkfs.ext4 -L rootfs rootfs.ext4

第1行进入rootfs目录。

第2行使用dd命令创建一个名为rootfs.ext4的磁盘由于根文件系统比较大因此count设置为 1024也就是根文件系统总空间为1GB这个是可以调整的要根据自己的实际根文件系统大小调整count的值只要空间不超过开发板所使用的8GB EMMC即可。最好不要设置太大否则烧写会很慢

第3行使用mkfs.ext4将rootfs.ext4磁盘格式化为ext4格式

完成以后就会生成名为“rootfs.ext4”的磁盘。

将系统镜像拷贝到ext4磁盘中

首先创建一个目录用来挂载前面制作制作出来的rootfs.ext4比如教程中创建目录/mnt/rootfs命令如下

sudo mkdir /mnt/rootfs

接下来使用mount命令将rootfs.ext4挂载到/mnt/rootfs目录下命令如下

cd /home/zuozhongkai/linux/atk-mp1/linux/rootfs
sudo mount rootfs.ext4 /mnt/rootfs/

挂载成功以后就将/home/zuozhongkai/linux/nfs/rootfs目录下的所有根文件系统文件拷贝到/mnt/bootfs目录下命令如下

cd /home/zuozhongkai/linux/nfs/rootfs/
sudo cp * /mnt/rootfs/ -drf

拷贝完成后使用umount卸载/mnt/rootfs即可命令如下

sudo umount /mnt/rootfs

至此根文件系统就已经打包到rootfs.ext4中稍后使用STM32CubeProgrammer软件将其烧写到EMMC里面。烧写之前最好在Windows下打开rootfs.ext4看一下看看是否已经将根文件系统打包进去如下图所示
bootfs.ext4

烧写到EMMC

接下来就是将上一小节打包好的ext4格式的根文件系统 rootfs.ext4烧写到开发板的EMMC里面使用STM32CubeProgrammer软件完成此操作。rootfs.ext4拷贝到以前创建的images目录下如下图所示
images目录
修改FlashLayout文件tf-a.tsv在后面加入rootfs.ext4烧写脚本如下图所示
修改后的FlashLayout
上图中第8行就是根文件系统rootfs.ext4的烧写脚本设置好以后就可以使用STM32CubeProgrammer烧写系统了烧写完成以后设置拨码开关从EMMC启动。启动以后进
入uboot的命令行设置bootcmd和bootargs这两个环境变量命令如下

setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk1p3 rootwait rw'
saveenv
boot

注意bootargs环境变量里面root的值为/dev/mmcblk1p3说明根文件系统存放在mmcblk1的第3个分区。正点原子出厂系统的根文件系统是存放在mmcblk2的第3个分区但这里不是这是因为正点原子出厂系统使能了SDIO WIFI而SDIO WIFI也是SDIO接口如果使能了SDIO WIFI那么SDIO WIFI所使用的那个SDIO接口就变成了mmcblk1而EMMC所使用的SDIO接口就会变为mmcblk2。

设置以后启动系统此时就会从EMMC里面加载Linux系统镜像并且正确读取到根系统如下图所示
EMMC启动系统
从上图可以看出正常进入根文件系统。至此 Linux系统移植就全部完成包括tf-a、uboot、Linux kernel和rootfs

总结

这一章学习的是怎么用busybox来构建根文件系统。总体的构建过程不是很难

nfs/rootfs中创建根文件系统

首先修改Makefole来添加ARCH和CROSS_COMPILE编译器然后修改busybox的中文字符支持主要就是打开libbb/printable_string.c找到printable_string2函数把其中的>=0x7f相关删掉就可以了libbb/unicode.c中的unicode_conv_to_printable2也是同理。

然后配置busybox先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(17 kb)及以下都勾选最后在Settings的Support Unicode的Check $LC_ALL, $LC_CTYPE and $LANG environment variables选中来支持中文。搞定之后make再make install CONFIG_PREFIX=/home/zuozhongkai/linux/nfs/rootfs编译一下就可以了。

因为是动态链接需要手动添加lib。

在rootfs的/lib添加/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib中的so其中ld-linux-armhf.so.3需要删掉重新拷贝

cp *so* /home/zuozhongkai/linux/nfs/rootfs/lib/ -d
cp ld-linux-armhf.so.3 /home/zuozhongkai/linux/nfs/rootfs/lib/

之后拷贝/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/lib中的so和.a

cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/lib/ -d

在rootfs的usr/lib添加在/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/usr/lib拷贝so和.a库

cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -d

之后在rootfs手动创建dev、proc、mnt、sys、tmp、etc和root等就可以了。

nfs中测试

首先修改nfs版本在/etc/default/nfs-kernel-server文件中最后添加

RPCNFSDOPTS="--nfs-version 2,3,4 --debug --syslog"

之后重启nfs服务

sudo /etc/init.d/nfs-kernel-server restart

然后设置uboot下的bootargs环境变量

setenv bootargs 'console=ttySTM0,115200 root=/dev/nfs nfsroot=192.168.1.249:/home/zuozhongkai/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.250:192.168.1.249:192.168.1.1:255.255.255.0::eth0:off' //设置 bootargs
saveenv //保存环境变量
## 完善rootfs 添加/etc/init.d/rcS文件
示例代码18.4.1.1 /etc/init.d/rcS文件 
1 #!/bin/sh 
2 
3 PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH 
4 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib 
5 export PATH LD_LIBRARY_PATH 
6 
7 mount -a 
8 mkdir /dev/pts 
9 mount -t devpts devpts /dev/pts 
10 
11 echo /sbin/mdev > /proc/sys/kernel/hotplug
12 mdev -s

设置权限777后即可。

然后创建/etc/fstab文件

示例代码18.4.2.1 /etc/fstab文件 
1 #<file system> <mount point> <type> <options> <dump> <pass>
2 proc /proc proc defaults 0 0 
3 tmpfs /tmp tmpfs defaults 0 0 
4 sysfs /sys sysfs defaults 0 0

最后创建/etc/inittab文件

示例代码18.4.3.1 /etc/inittab文件 
1 #etc/inittab 
2 ::sysinit:/etc/init.d/rcS 
3 console::askfirst:-/bin/sh 
4 ::restart:/sbin/init 
5 ::ctrlaltdel:/sbin/reboot 
6 ::shutdown:/bin/umount -a -r
7 ::shutdown:/sbin/swapoff -a

之后去配置一下Linux的内核(我自己测试的时候这个选项是直接选中的)路径如下

Location:
-> Device Drivers
-> Generic Driver Options
->Support for uevent helper //选中

一些软件测试等具体操作就翻一下上文就好了不赘述。

烧写到EMMC

自行创建一个rootfs文件夹之后使用如下命令创建ext4磁盘

dd if=/dev/zero of=rootfs.ext4 bs=1M count=1024
mkfs.ext4 -L rootfs rootfs.ext4

然后与之前一样来挂载这个rootfs.ext4

sudo mkdir /mnt/rootfs
cd /home/zuozhongkai/linux/atk-mp1/linux/rootfs
sudo mount rootfs.ext4 /mnt/rootfs/
cd /home/zuozhongkai/linux/nfs/rootfs/
sudo cp * /mnt/rootfs/ -drf
sudo umount /mnt/rootfs

之后再Windows中修改FlashLayout并设置uboot的环境变量bootcmd和bootargs

P 0x22 rootfs FileSystem mmc1 0x04290000 rootfs.ext4
setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk1p3 rootwait rw'
saveenv
boot

总结之总结

到这里其实已经构建了一套Linux的最小系统了从之前的TF-A(I.mx6ull没有)然后是uboot、Linux kernel到现在的rootfs齐活了。但是busybox构建不是那么方便之后讲解更实用的buildroot来构建然后应该系统移植就齐活了之后的就是一些驱动应用移植笔记也不会那么详细了。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux