目录
BusyBox 简介
编译 BusyBox 构建根文件系统
1、修改 Makefile,添加编译器
2、busybox 中文字符支持
3、配置 busybox
4、编译 busybox
5.根文件系统添加 lib 库
1、向 rootfs 的“/lib”目录添加库文件
2、向 rootfs 的“usr/lib”目录添加库文件
3.创建其他文件夹
根文件系统初步测试
完善根文件系统
1.创建/etc/init.d/rcS 文件
2.创建/etc/fstab 文件
3.创建/etc/inittab 文件
开机自启动
外网连接测试
BusyBox 简介
根文件系统里面就是一堆的可执行文件和其他文件组成的?难道我们得一
个一个的从网上去下载这些文件?显然这是不现实的!那么有没有人或者组织专门干这个事呢?
他们负责“收集”这些文件,然后将其打包,像我们这样的开发者可以直接拿来用。答案是有
的,它就叫做
BusyBox
!其名字分为“
Busy
”和“
Box
”,也就是忙碌的盒子。盒子是用来放东
西的,忙碌的是因为它要提供根文件系统所需的文件,所以忙碌。
BusyBox
是一个集成了大量
的
Linux
命令和工具的软件,像
ls
、
mv
、
ifconfig
等命令
BusyBox
都会提供。
BusyBox
就是一
个大的工具箱,这个工具箱里面集成了
Linux
的许多工具和命令。一般下载
BusyBox
的源码,
然后配置
BusyBox
,选择自己想要的功能,最后编译即可。
BusyBox
可以在其官网下载到,官网地址为:
https://busybox.net/
,官网比较简陋,
在官网左侧的“
Get BusyBox
”栏有一行“
Download Source
”,点击“
Download Source
”即
可打开
BusyBox
的下载页,
编译 BusyBox 构建根文件系统
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
2、busybox 中文字符支持
所以我们需要修改
busybox
源码,取消
busybox
对中文显示的限制,打开文件
busybox-
1.29.0/libbb/,找到函数 printable_string
,修改内容如下:
12 const char* FAST_FUNC printable_string(uni_stat_t *stats, const char
*str)
13 {
14 char *dst;
15 const char *s;
16
17 s = str;
18 while (1) {
......
30 if (c < ' ')
31 break;
32 /* 注释掉下面这个两行代码 */
33 /* if (c >= 0x7f)
34 break; */
35 s++;
36 }
37
38 #if ENABLE_UNICODE_SUPPORT
39 dst = unicode_conv_to_printable(stats, str);
40 #else
41 {
42 char *d = dst = xstrdup(str);
43 while (1) {
44 unsigned char c = *d;
45 if (c == '\0')
46 break;
47 /* 修改下面代码 */
48 /* if (c < ' ' || c >= 0x7f) */
49 if( c < ' ')
50 *d = '?';
51 d++;
52 }
......
59 #endif
60 return auto_string(dst);
61 }
接着打开文件 busybox-1.29.0/libbb/unicode.c,修改如下内容:
1003 static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t
*stats, const char *src, unsigned width, int flags)
1004 {
1005 char *dst;
1006 unsigned dst_len;
1007 unsigned uni_count;
1008 unsigned uni_width;
1009
1010 if (unicode_status != UNICODE_ON) {
1011 char *d;
1012 if (flags & UNI_FLAG_PAD) {
1013 d = dst = xmalloc(width + 1);
......
1022 /* 修改下面一行代码 */
1023 /* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
1024 *d++ = (c >= ' ') ? c : '?';
1025 src++;
1026 }
1027 *d = '\0';
1028 } else {
1029 d = dst = xstrndup(src, width);
1030 while (*d) {
1031 unsigned char c = *d;
1032 /* 修改下面一行代码 */
1033 /* if (c < ' ' || c >= 0x7f) */
1034 if(c < ' ')
1035 *d = '?';
1036 d++;
1037 }
1038 }
......
1044 return dst;
1045 }
......
1047
1048 return dst;
1049 }
最后还需要配置
busybox 来使能 unicode
码。
3、配置 busybox
有以下几种
配置选项:
①、
defconfig
,缺省配置,也就是默认配置选项。
②、
allyesconfig
,全选配置,也就是选中
busybox
的所有功能。
③、
allnoconfig
,最小配置。
我们一般使用默认配置即可,因此使用如下命令先使用默认配置来配置一下
busybox
:
make defconfig
busybox
也支持图形化配置,通过图形化配置我们可以进一步选择自己想要的功能,输入
如下命令打开图形化配置界面:
make menuconfig
选项“
Build static binary (no shared libs)
”用来决定是静态编译
busybox
还是动态编译,静
态编译的话就不需要库文件,但是编译出来的库会很大。动态编译的话要求根文件系统中有库
文件,但是编译出来的
busybox
会小很多。这里我们不能采用静态编译!因为采用静态编译的
话
DNS
会出问题!无法进行域名解析。
不选择“Build static binary (no shared libs)
4、编译 busybox
make
make install CONFIG_PREFIX=/home/zuozhongkai/linux/nfs/rootfs
编译完成以后会在
busybox
的所有工具和文件就会被安装到
rootfs
目录中,
rootfs
目录内容
如图
38.2.2.9
所示:
rootfs
目录下有
bin
、
sbin
和
usr
这三个目录,以及
linuxrc
这个文件。前面说过 Linux
内核
init
进程最后会查找用户空间的
init
程序,找到以后就会运行这个用 户空间的 init
程序,从而切换到用户态。如果
bootargs 设置 init=/linuxrc
,那么
linuxrc
就是可以 作为用户空间的 init
程序,所以用户态空间的
init
程序是
busybox
来生成的。
busybox
的工作就完成了,但是此时的根文件系统还不能使用,还需要一些其他的文件,我
们继续来完善
rootfs
。
5.根文件系统添加 lib 库
1、向 rootfs 的“/lib”目录添加库文件
Linux
中的应用程序一般都是需要动态库的,当然你也可以编译成静态的,但是静态的可
执行文件会很大。如果编译为动态的话就需要动态库,所以我们需要向根文件系统中添加动态
库。在
rootfs
中创建一个名为“
lib
”的文件夹,命令如下:
mkdir lib
lib
文件夹创建好了,库文件从哪里来呢?
lib 库文件从交叉编译器中获取
,前面我们搭建
交叉编译环境的时候将交叉编译器存放到了“
/usr/local/arm/
”目录中。交叉编译器里面有很多
的库文件,这些库文件具体是做什么的我们作为初学者肯定不知道,既然我不知道那就简单粗
暴的把所有的库文件都放到我们的根文件系统中。
进入如下路径对应的目录:
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux
gnueabihf/libc/lib
将此目录下所有的
*so*
和
.a 文件都拷贝到 rootfs/lib
目录中
cp *so* *.a /home/zuozhongkai/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”查看此文件详细信息,如图 38.2.3.1 所示:
可以看出,
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
文件删除掉,命令
如下:
然后重新进入到
/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/zuozhongkai/linux/nfs/rootfs/lib/
继续进入如下目录中:
/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/zuozhongkai/linux/nfs/rootfs/lib/ -d
rootfs/lib 目录的库文件就这些了,完成以后的 rootfs/lib 目录如图 所示:
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/zuozhongkai/linux/nfs/rootfs/usr/lib/ -d
完成以后的 rootfs/usr/lib 目录如图 所示:
至此,根文件系统的库文件就全部添加好了,可以使用“
du
”命令来查看一下
rootfs/lib
和
rootfs/usr/lib
这两个目录的大小,命令如下:
cd rootfs
//
进入根文件系统目录
du ./lib ./usr/lib/ -sh
//
查看
lib
和
usr/lib
这两个目录的大小
可以看出
lib
和
usr/lib
这两个文件的大小分别为
57MB
和
67MB
,加起来就是
57+67=124MB
。
非常大!所以正点原子的
256MB
和
512MB
的
NAND
核心版就不是给初学者准备的,而是给大
批量采购的企业准备的,还是那句话,
初学者选择 EMMC 版本的。
3.创建其他文件夹
在根文件系统中创建其他文件夹,如
dev
、
proc
、
mnt
、
sys
、
tmp
和
root
等,创建完成以后
如图
所示:
根文件系统初步测试
测试方法就是使用
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
地址,不使用。
根据上面的格式
bootargs
环境变量的
root
值如下:
root=/dev/nfs nfsroot=192.168.1.250:/home/zuozhongkai/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.1.250:
/home/zuozhongkai/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' //
设置
bootargs
saveenv //保存环境变量
设置好以后使用“boot”命令启动 Linux 内核,
可以看出
ls
命令工作正常!那么是不是说明我们的
rootfs
就制作成功了呢?大家注意,在
进入根文件系统的时候会有下面这一行错误提示:
can't run '/etc/init.d/rcS': No such file or directory
提示很简单,说是无法运行“
/etc/init.d/rcS”这个文件,因为这个文件不存在。
完善根文件系统
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
文档。
示例代码
38.4.1.1
中的
rcS
文件内容是最精简的,大家如果去看
Ubuntu
或者其他大型
Linux
操作系统中的
rcS
文件,就会发现其非常复杂。因为我们是初次学习,所以不用搞这么复杂的,
而且这么复杂的
rcS
文件也是借助其他工具创建的,比如
buildroot
等。
创建好文件
/etc/init.d/rcS
以后一定要给其可执行权限!
使用如下命令给予
/ec/init.d/rcS
可执行权限:
设置好以后就重新启动 Linux 内核,
提示找不到
/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
文件中输入如下内容:
#
<
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,可以看出,启动成功,而且没有任何错误提示。但是我们要还需要创建一个 文件/etc/inittab.
3.创建/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
支持的动作如表
38.4.3.1
所
示:
<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/inittab
文件创建好以后就可以重启开发板即可,至此!根文件系统要创建的文件就已经
全部完成了。
开机自启动
进入根文件系统的时候
会运行
/etc/init.d/rcS
这个
shell
脚本,因此我们可以在这个脚本里面添加自启动相关内容。添加
完成以后的
/etc/init.d/rcS
文件内容如下
#
开机自启动
cd
/
drivers
./
hello
&
cd
/
外网连接测试
可以看出,测试失败,提示
www.baidu.com
是个“
bad address
”,也就是地址不对,显然我
们的地址是正确的。之所以出现这个错误提示是因为
www.baidu.com
的地址解析失败了,并没
有解析出其对应的
IP
地址。我们需要配置域名解析服务器的
IP
地址,一般域名解析地址可以
设置为所处网络的网关地址,比如
192.168.1.1
。也可以设置为
114.114.114.114
,这个是运营商
的域名解析服务器地址。
在
rootfs
中新建文件
/etc/resolv.conf
,然后在里面输入如下内容:
nameserver
114.114.114.114
nameserver
192.168.1.1
设置很简单,
nameserver
表示这是个域名服务器,设置了两个域名服务器地址:
114.114.114.114
和
192.168.1.1
,大家也可以改为其他的域名服务器试试。如果使用“
udhcpc
”命
令自动获取
IP
地址,“
udhcpc
”命令会修改
nameserver
的值,一般是将其设置为对应的网关地
址。修改好以后保存退出,重启开发板!重启以后重新
ping
一下百度官网,
至此!我们的根文件系统就彻底的 制作完成,这个根文件系统最好打包保存一下.