根文件系统首先是内核启动时所mount(挂载)的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。
一、根文件系统简介
cpp
/ # cd /
/ # ls
bin etc linuxrc music sbin tmp
dev include minicom.log proc share usr
drivers lib mnt root sys var
/ #
1、/bin 目录
bin 文件就是可执行文件。所以此目录下存放着系统需要的可执行文件,一般都是一些命令,比如ls、mv 等命令。此目录下的命令所有的客户都可以使用。
2、/dev 目录
dev 是device 的缩写,所以此目录下的文件都是和设备有关的,此目录下的文件都是设备文件。在Linux 下一切皆文件,即使是硬件设备,也是以文件的形式存在的
3、/etc 目录
此目录下存放着各种配置文件,大家可以进入Ubuntu 的etc 目录看一下,里面的配置文件非常多!但是在嵌入式Linux 下此目录会很简洁。
cpp
/ # cd etc/
/etc # ls
fstab inittab profile ts.conf
init.d passwd resolv.conf
/etc #
4、/lib 目录
lib 是library 的简称,也就是库的意思,因此此目录下存放着Linux 所必须的库文件。这些库文件是共享库,命令和用户编写的应用程序要使用这些库文件。
5、/mnt 目录
临时挂载目录,一般是空目录,可以在此目录下创建空的子目录,比如/mnt/sd、/mnt/usb,这样就可以将 SD 卡或者 U 盘挂载到 /mnt/sd 或者 /mnt/usb 目录中。
6、/proc 目录(process(进程)文件系统)
此目录一般是空的,当Linux 系统启动以后会将此目录作为proc 文件系统的挂载点,proc是个虚拟文件系统,没有实际的存储设备。proc 里面的文件都是临时存在的,一般用来存储系统运行信息文件。
7、/usr 目录
要注意,usr 不是user 的缩写,而是Unix Software Resource 的缩写,也就是Unix 操作系统软件资源目录,/usr 目录下也存放着很多软件,一般系统安装完成以后此目录占用的空间最多。
8、/var 目录
此目录存放一些可以改变的数据。
9、/sbin 目录
此目录页用户存放一些可执行文件,但是此目录下的文件或者说命令只有管理员才能使用,主要用户系统管理。
10、/sys 目录(system file system(系统文件系统))
系统启动以后此目录作为sysfs 文件系统的挂载点,sysfs 是一个类似于proc 文件系统的特殊文件系统,sysfs 也是基于ram 的文件系统,也就是说它也没有实际的存储设备。此目录是系统设备管理的重要目录,此目录通过一定的组织结构向用户提供详细的内核数据结构信息。
11、/opt目录
可选的文件、软件存放区,由用户选择将哪些文件或软件放到此目录中。
二、BusyBox 构建根文件系统
BusyBox 是一个集成了大量的Linux 命令和工具的软件,像ls、mv、ifconfig 等命令BusyBox 都会提供。BusyBox 就是一个大的工具箱,这个工具箱里面集成了Linux 的许多工具和命令。一般下载 BusyBox 的源码,然后配置 BusyBox,选择自己想要的功能,最后编译即可。
1、下载源码,编译BusyBox 构建根文件系统
一般我们在Linux 驱动开发的时候都是通过nfs 挂载根文件系统的,当产品最终上市开卖的时候才会将根文件系统烧写到EMMC 或者NAND 中。
cpp
/**在 nfs 文件夹下创建 rootfs*/
mkdir rootfs
源码解压:

修改Makefile 中的 ARCH,CROSS_COMPILE 的值
cpp
/*使用绝对路径,防止编译出错*/
CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
ARCH ?= arm
2、修改为busybox 中文字符支持
修改busybox源码,取消对中文的限制。打开文件busybox-1.29.0/libbb**/printable_string.c** ,找到函数printable_string,缩减后的函数内容如下:
cpp
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') {
break;
}
if (c < ' ')
break;
if (c >= 0x7f) //当字符大于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) //如果支持UNICODE 码的话,当字符大于0X7F 就直接输出'?'
*d = '?';
d++;
}
}
#endif
return auto_string(dst);
}
修改后:主要就是禁止字符大于0X7F 以后break 和输出'?'。
cpp
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') {
break;
}
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++;
}
}
#endif
return auto_string(dst);
}
接着打开文件busybox-1.29.0/libbb/unicode.c:
cpp
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);
......
*d++ = (c >= ' ' && c < 0x7f) ? c : '?'; //当字符大于0X7F 以后,*d++就为'?'
src++;
}
*d = '\0';
} else {
d = dst = xstrndup(src, width);
while (*d) {
unsigned char c = *d;
if (c < ' ' || c >= 0x7f) //当字符大于0X7F 以后,*d 也为'?'
*d = '?';
d++;
}
}
......
return dst;
}
......
return dst;
}
修改为:主要是禁止字符大于0X7F 的时候设置为'?'。
cpp
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);
......
/* 修改下面一行代码 */
/* *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++;
}
}
......
return dst;
}
......
return dst;
}
3、配置busybox
根我们编译Uboot、Linux kernel 一样,我们要先对busybox 进行默认的配置,有以下几种配置选项:
①、defconfig,缺省配置,也就是默认配置选项。
②、allyesconfig,全选配置,也就是选中busybox 的所有功能。
③、allnoconfig,最小配置。
先默认配置一遍:
cpp
make defconfig
进行图形化配置:
1)配置为静态编译
cpp
Location:
-> Settings
-> Build static binary (no shared libs)
用来决定是静态编译busybox 还是动态编译 ,静态编译的话就不需要库文件 ,但是编译出来的库会很大。动态编译的话要求根文件系统中有库文件,但是编译出来的busybox 会小很多。这里不能采用静态编译!因为采用静态编译的话DNS 会出问题!无法进行域名解析

2)使能 VI 编辑
"vi-style line editing commands " 是指在命令行输入时,可以使用 vi(或 Vim)编辑器的键位 来编辑当前输入的命令行。
cpp
Location:
-> Settings
-> vi-style line editing commands

3)轻量级模块加载
✅ Simplified modutils 是什么?
BusyBox 内置一套"简化版的 Linux 模块工具(modutils)",替代传统的:
insmod
rmmod
modprobe
depmod等 Linux 内核模块管理工具。
Simplified modutils = BusyBox 的轻量级模块加载/卸载工具。
它不包含完整的依赖处理和高级功能,只能执行基本的模块加载/卸载。
📌 开启它可以做什么?
启用 Simplified modutils 后,BusyBox 会为你提供简单的:
insmod(加载 .ko 模块)
rmmod(卸载模块)
modprobe(按名字加载模块,但功能简化)
depmod(生成依赖,但极简)这些工具在 嵌入式 Linux(如路由器、OpenWrt、initramfs、BusyBox 系统)中非常常见。
📉 有什么限制?
因为是 simplified(简化版),所以:
不支持复杂的模块依赖解析
不支持 modprobe 的高级功能(如别名、blacklist)
不完全兼容标准的 kmod 或 module-init-tools
适合 极小系统,不适合完整桌面或服务器。
🧭 什么时候需要开启?
你通常在:
initramfs
嵌入式系统
BusyBox 制作的最小 rootfs
没有 kmod 的小系统
才会启用它。
如果你的系统已经有完整的
kmod包,就 不要开启,避免冲突。
cpp
Location:
-> Linux Module Utilities
-> Simplified modutils

4)使能设备管理器
✅ mdev 是什么?
mdev是 BusyBox 提供的 轻量级设备管理器 ,是标准 Linuxudev的替代品。它负责:
在
/dev/下创建与内核设备对应的设备节点热插拔设备时自动创建/删除节点
读取规则文件(例如
/etc/mdev.conf)决定如何命名、权限、运行脚本在开机时自动扫描
/sys并创建设备节点适用于 嵌入式系统、小型 rootfs、initramfs。
📌 mdev 的典型用途
在精简 Linux 里,因为不能用体积庞大的 udev,mdev 就取代它来:
处理 USB 插拔(自动生成
/dev/sdX,/dev/ttyUSB0等)管理块设备、字符设备
创建驱动加载后的节点(如
/dev/i2c-*,/dev/spidev*)在插拔时触发脚本 (hotplug events)
对很多嵌入式系统而言是 必选组件。
📁 为什么"确保下面的全部选中"?
进入 mdev 选项后,通常有子选项:
Support /etc/mdev.conf
Support mdev -s (coldplug)
Support hotplug events
Support firmware loading
Support sysfs trigger这些功能用于:
✔ 创建正确的设备节点
✔ 处理开机冷插(扫描 sysfs)
✔ 处理插拔热插事件
✔ 让用户自定义规则
✔ 加载固件(给驱动用)
默认 BusyBox 都会选中这些,否则 mdev 功能不完整。
✨ 一句简单总结
mdev = BusyBox 版本的 udev,用来管理 /dev 下的设备节点。
如果你要做嵌入式或 minimal Linux,这个必须开,并确保相关子选项都打开。
cpp
Location:
-> Linux System Utilities
-> mdev (16 kb) //确保下面的全部选中,默认都是选中的

5)使能busybox 的unicode 编码以支持中文
cpp
Location:
-> Settings
-> Support Unicode //选中
-> Check $LC_ALL, $LC_CTYPE and $LANG environment variables //选中

根据自己的实际需求来配置。
4、编译busybox
cpp
make
make install CONFIG_PREFIX=/home/v11/linux/nfs/rootfs
编译安装后:

其中:linuxrc 可以作为用户空间的init 程序,即用户态空间的init 程序是busybox 来生成的。
5、向根文件系统添加lib 库
1)向 rootfs 的 "/lib" 目录添加库文件
Linux 中的应用程序一般都是需要动态库的,向根文件系统中添加动态库。在rootfs 中创建一个名为"lib"的文件夹。
cpp
mkdir lib
lib 文件夹创建好了,库文件从哪里来呢?lib 库文件从交叉编译器中获取,前面我们搭建交叉编译环境的时候将交叉编译器存放到了"/usr/local/arm/"目录中。
cpp
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linuxgnueabihf/libc/lib
此目录下有很多的*so*(*是通配符)和.a 文件,这些就是库文件,将此目录下所有的***so*和.a**文件都拷贝到rootfs/lib 目录中:
cpp
cp *so* *.a /home/v11/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"查看此文件详细信息:

重新复制 ld-linuxarmhf.so.3,只是不复制软链接即可,先将rootfs/lib 中的ld-linux-armhf.so.3 文件删除掉,命令如下:
cpp
rm ld-linux-armhf.so.3
然后重新进入到/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/armlinux-gnueabihf/libc/lib 目录中,重新拷贝ld-linux-armhf.so.3,命令如下:
cpp
cp ld-linux-armhf.so.3 /home/v11/linux/nfs/rootfs/lib/
检查是否还是软链接:

/***********************************************************************************************************/
继续进入如下目录中:
cpp
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib
拷贝文件:
cpp
cp *so* *.a /home/v11/linux/nfs/rootfs/lib/ -d
完成以后:

2)向rootfs 的"usr/lib"目录添加库文件
在rootfs 的 usr 目录下创建一个名为 lib 的目录 ,将如下目录中的库文件拷贝到rootfs/usr/lib目录下:
cpp
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
cpp
cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -d
拷贝完成后:

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

3)创建其他文件夹
在根文件系统中创建其他文件夹,如dev、proc、mnt、sys、tmp 和root 等。创建完以后为:

6、根文件系统初步测试
测试方法就是使用NFS 挂载,uboot 里面的bootargs 环境变量会设置"root"的值,所以我们将root 的值改为NFS 挂载即可。
格式为:
cpp
root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
📌 参数说明表
1. root= 参数
参数 说明 /dev/nfs告诉内核根文件系统通过 NFS 挂载,而不是本地磁盘。
2. nfsroot= 参数
字段 示例 说明 <server-ip>192.168.1.10NFS 服务器的 IP;可省略,省略时会从 DHCP 获取。 <root-dir>/export/rootfsNFS 服务器上根文件系统的路径(必填)。 <nfs-options>vers=3,proto=tcpNFS 挂载的可选参数,例如协议版本、挂载方式等。 格式:
nfsroot=<server-ip>:<root-dir>,<nfs-options>
3. ip= 参数
格式:
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0>:<dns1>
字段 示例 说明 <client-ip>192.168.1.100启动设备(客户端)的 IP 地址。 <server-ip>192.168.1.10NFS 或 TFTP 服务器 IP。 <gw-ip>192.168.1.1默认网关 IP;无则填 0.0.0.0。<netmask>255.255.255.0子网掩码。 <hostname>myboard客户端主机名。 <device>eth0使用的网络设备(如 eth0、eth1)。 <autoconf>off/dhcp网络配置方式: off手动配置,dhcp自动获取。<dns0>8.8.8.8主 DNS 服务器(可选)。 <dns1>8.8.4.4次 DNS 服务器(可选)。
设置为:
cpp
root=/dev/nfs nfsroot=192.168.1.250:/home/v11/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
✅ 逐项详细解释(表格形式)
1. root=/dev/nfs
参数 含义 root=/dev/nfs告诉内核根文件系统是通过 NFS 挂载,而不是本地磁盘或 initramfs。
2. nfsroot=192.168.1.250:/home/v11/linux/nfs/rootfs,proto=tcp rw
字段 示例值 含义 <server-ip>192.168.1.250NFS 服务器 IP 地址 <root-dir>/home/v11/linux/nfs/rootfsNFS 上根文件系统所在目录 proto=tcpproto=tcp使用 TCP 协议挂载 NFS(通常更可靠) rwrw以可读写方式挂载根文件系统
3. ip=192.168.1.251:192.168.1.250:192.168.1.1:255.255.255.0::eth0:off
格式为:
ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>
字段 示例值 含义 <client-ip>192.168.1.251启动板卡(客户端)的 IP <server-ip>192.168.1.250NFS/TFTP 服务器的 IP <gwip>192.168.1.1默认网关 IP <netmask>255.255.255.0子网掩码 <hostname>(空,表示不设置主机名) 在参数中两个冒号连在一起表示空字段: ::<device>eth0使用的网络接口 <autoconf>off关闭自动获取 IP(DHCP),采用上面手动配置的值 DNS 参数没写,默认空(可省略)。
进入uboot 命令行模式,然后重新设置bootargs 环境变量,命令如下:
cpp
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 //保存环境变量
设置好后重启开发板,会提示:
cpp
can't run '/etc/init.d/rcS': No such file or directory
说明无法运行 "/etc/init.d/rcS" 这个文件,因为这个文件不存在
7、完善根文件系统
1)创建/etc/init.d/rcS 文件(Linux 系统启动时执行的初始化脚本)
rcS 是个shell 脚本,Linux 内核启动以后需要启动一些服务,而rcS 就是规定启动哪些文件的脚本文件。在rootfs 中创建/etc/init.d/rcS 文件,然后在rcS 中输入如下所示内容:
bash
#!/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 # 按 /etc/fstab 挂载所有文件系统
mkdir /dev/pts # 创建 pts 目录(终端伪设备)
mount -t devpts devpts /dev/pts # 挂载 devpts,支持终端、SSH 等
echo /sbin/mdev > /proc/sys/kernel/hotplug # 设置内核热插拔处理程序为 mdev
mdev -s # 扫描 /sys 创建所有设备节点(coldplug)
给予该文件权限,并重启开发板;遇到如下问题:

2)创建/etc/fstab 文件(Linux 系统的文件系统挂载表(file system table) 配置文件)
在rootfs 中创建/etc/fstab 文件,fstab 在Linux 开机以后自动配置哪些需要自动挂载的分区,格式如下:
bash
<file system> <mount point> <type> <options> <dump> <pass>
📌 fstab 参数说明表
字段 示例 含义 <file system> /dev/sda1,UUID=xxxx,192.168.1.10:/nfs/rootfs要挂载的文件系统来源,可以是设备、UUID、LABEL 或网络路径 <mount point> /,/home,/mnt/nfs挂载到本地系统的哪个目录 <type> ext4,vfat,nfs,tmpfs文件系统类型 <options> defaults,rw,noatime,nosuid挂载选项,用逗号分隔,可设置读写模式、权限策略、缓存行为等 <dump> 0或1是否被 dump工具备份,几乎所有现代系统用0<pass> 0,1,2启动时 fsck检查顺序:0=不检查,1=优先检查(通常是根),2=其他分区按顺序检查
📘 补充说明(简洁易懂)
✔
<file system>
可以是设备路径:
/dev/mmcblk0p2或 UUID:
UUID=xxxx或 NFS:
192.168.1.88:/nfs/root或特种 fs:
tmpfs✔
<mount point>文件系统最终呈现出来的位置。
✔
<type>常见类型:
本地:
ext4,ext3,xfs,btrfs,vfat网络:
nfs,cifs特殊:
tmpfs,proc,sysfs✔
<options>常用示例:
defaults(rw, suid, dev, exec, auto, nouser, async)
rw(读写)
ro(只读)
noatime(减少磁盘写入)
sync(同步写入)
nolock(NFS 不锁)
uid=0,gid=0(fat 系统设权限)✔
<dump>几乎永远填
0,只有老系统工具会使用。✔
<pass>用于启动时
fsck检查顺序:
根分区:
1其他分区:
2不检查:
0
在创建的文件中输入如下内容:
bash
#<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
解释:
📌 逐行解释(表格)
文件系统 挂载点 类型 选项 dump pass 作用说明 proc/procprocdefaults00挂载 proc 虚拟文件系统,提供内核和进程信息,如 /proc/cpuinfo、/proc/meminfo。启动 Linux 必须。tmpfs/tmptmpfsdefaults00在内存中创建临时目录 /tmp,速度快,用于临时文件、缓存。关机后自动清空。sysfs/syssysfsdefaults00挂载 sysfs,用于提供内核对象、驱动、设备信息(如 /sys/class、/sys/devices)。现代 Linux 必须。
📘 补充说明
- proc 文件系统
是一种虚拟文件系统,不存在于磁盘上
内核把许多运行信息放在
/proc里(比如进程状态、CPU 信息、内存信息等)系统和工具(如 ps、top、free)都依赖它
没有
/proc系统会无法正常运行。
- tmpfs
存在于内存(RAM)中,而不是硬盘
/tmp被许多软件用作临时工作目录tmpfs 在内存中,速度特别快
适用于嵌入式系统或无硬盘系统。
- sysfs
与驱动、设备、内核对象相关
很多工具(如 udev/mdev)需要
/sys才能工作也用于查看硬件信息
例如:
/sys/class/net/eth0用于查看网络设备属性。
3)创建/etc/inittab 文件(Linux 系统启动初始化程序(init) 的配置文件)
inittab 的详细内容可以参考busybox 下的文件examples/inittab。init 程序会读取/etc/inittab这个文件,inittab 由若干条指令组成。每条指令的结构都是一样的,由以":"分隔的4 个段组成,格式如下:
bash
<id>:<runlevels>:<action>:<process>
/*
<id>:每个指令的标识符,不能重复。
<runlevels>:对busybox 来说此项完全没用,所以空着。
<action>:动作,用于指定<process>可能用到的动作。
<process>:具体的动作,比如程序、脚本或命令等。
*/

参考busybox 的examples/inittab 文件,我们也创建一个/etc/inittab,在里面输入如下内容:
bash
#etc/inittab
::sysinit:/etc/init.d/rcS //系统启动以后运行/etc/init.d/rcS 这个脚本文件。
console::askfirst:-/bin/sh //将console 作为控制台终端,也就是ttymxc0。
::restart:/sbin/init //重启的话运行/sbin/init。
::ctrlaltdel:/sbin/reboot //按下ctrl+alt+del 组合键的话就运行/sbin/reboot
::shutdown:/bin/umount -a -r //关机的时候执行/bin/umount,也就是卸载各个文件系统
::shutdown:/sbin/swapoff -a //关机的时候执行/sbin/swapoff,也就是关闭交换分区。
/**************************************************************************************************************/
4)三者之间的关系
1️⃣ 各自作用回顾
文件 作用 /etc/inittab init 的配置文件,定义系统启动、关机、控制台 shell、Ctrl+Alt+Del 等行为。 /etc/fstab 文件系统挂载表,定义哪些设备或网络文件系统挂载到哪些目录,挂载类型、选项、启动检查顺序。 /etc/init.d/rcS 系统初始化脚本,由 inittab 调用,用来挂载文件系统、创建设备节点、加载模块、启动必要服务。
2️⃣ 关系与执行顺序
init 进程启动
Linux 内核启动完成后,会执行
/sbin/init(或 BusyBox init)。init 读取
/etc/inittab,决定启动流程。inittab 调用 rcS
::sysinit:/etc/init.d/rcS行告诉 init,在系统初始化阶段执行rcS脚本。rcS 是"冷启动"脚本,做基本初始化。
rcS 挂载文件系统
rcS 内部通常会执行:
mount -a这会读取
/etc/fstab,把根文件系统及其他分区、网络文件系统挂载好。挂载完成后,系统才能访问
/proc、/sys、/tmp、硬盘或 NFS 根目录。rcS 继续初始化系统
创建
/dev、/dev/pts等设备节点设置热插拔管理器(如 mdev)
加载内核模块
配置环境变量
控制台和多用户环境
- rcS 完成后,inittab 根据配置启动 shell、终端(tty)或进入多用户模式。
3️⃣ 总结它们的关系
bash内核启动 → init (/sbin/init) → 读取 /etc/inittab │ ├─ sysinit 阶段 → 调用 /etc/init.d/rcS │ ├─ mount -a → 挂载 /etc/fstab 定义的文件系统 ├─ 创建设备节点、加载模块 └─ 系统环境就绪
inittab:启动控制中心,告诉系统做什么
rcS:执行初始化操作的脚本
fstab:定义挂载规则,由 rcS 使用
可以理解为:inittab 是计划,rcS 是执行,fstab 是数据。
至此!根文件系统要创建的文件就已经全部完成了
8、测试根文件系统
1)软件运行测试
2)中文字符测试
3)开机自启动测试(在/etc/init.d/rcS文件里添加)
bash
#!/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 /
4)外网连接测试
在rootfs 中新建文件/etc/resolv.conf,然后在里面输入如下内容:
bash
/*nameserver 表示这是个域名服务器, 设置了两个域名服务器地址:*/
nameserver 114.114.114.114
nameserver 192.168.1.1
然后 ping 外网。
至此!我们的根文件系统就彻底的制作完成!!!
三、Buildroot 根文件系统构建
busybox 仅仅只是帮我们构建好了一些常用的命令和文件,像lib 库、/etc 目录下的一些文件都需要我们自己手动创建,而且busybox 构建的根文件系统默认没有用户名和密码设置。在后续的实验中,我们还要自己去移植一些第三方软件和库,比如alsa、iperf、mplayer 等等
busybox 构建的根文件系统不齐全,很多东西需要我们自行添加,比如lib 库文件。Buildroot 是另外一种实用的根文件系统构建方法,那就是使用buildroot 来构建根文件系统。
buildroot 和uboot、Linux Kernel 很类似,我们需要到其官网上下载源码,然后对其进行配置,比如设置交叉编译器、设置目标CPU 参数等,最主要的就是选择所需要的第三方库或软件。一切配置好以后就可以进行编译,编译完成了以后就会在一个文件夹里面存放好编译结果,也就是根文件系统。
1、buildroot 构建根文件系统
1)配置buildroot(进行图形化配置,配置完成以后可编译)

(1)配置Target options
首先配置Target options 选项,需要配置的项目和其对应的内容如下:
bash
Target options
-> Target Architecture = ARM (little endian)
-> Target Binary Format = ELF
-> Target Architecture Variant = cortex-A7
-> Target ABI = EABIhf
-> Floating point strategy = NEON/VFPv4
-> ARM instruction set = ARM
配置好后:

(2)配置Toolchain
此配置项用于配置交叉编译工具链,也就是交叉编译器,这里设置为我们自己所使用的交叉编译器即可。
bash
Toolchain
-> Toolchain type = External toolchain
-> Toolchain = Custom toolchain # 用户自带交叉编译器
-> Toolchain origin = Pre-installed toolchain # 已安装好的编译器
-> Toolchain path = /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf
-> Toolchain prefix = $(ARCH)-linux-gnueabihf # 前缀
-> External toolchain gcc version = 4.9.x
-> External toolchain kernel headers series = 4.1.x
-> External toolchain C library = glibc/eglibc
-> [*] Toolchain has SSP support? (NEW) # 选中
-> [*] Toolchain has RPC support? (NEW) # 选中
-> [*] Toolchain has C++ support? # 选中
-> [*] Enable MMU support (NEW) # 选中
bash
Toolchain:设置为Custom toolchain,表示使用用户自己的交叉编译器。
Toolchain origin:设置为Pre-installed toolchain,表示使用预装的交叉编译器。
Toolchain path:设置自己安装的交叉编译器绝对路径!buildroot 要用到。
Toolchain prefix:设置交叉编译器前缀,要根据自己实际所使用的交叉编译器来设置,比如我们使用的是
arm-linux-gnueabihf-gcc,因此前缀就是$(ARCH)-linux-gnueabihf,其中ARCH
我们前面已经设置为了arm。
(3)配置System configuration
此选项用于设置一些系统配置,比如开发板名字、欢迎语、用户名、密码等。需要配置的项目和其对应的内容如下:
bash
System configuration
-> System hostname = alpha_imx6ull # 平台名称,自行设置
-> System banner = Welcome to alpha i.mx6ull # 登录欢迎语
-> Init system = BusyBox # 使用 BusyBox 作为 init 系统
-> /dev management = Dynamic using devtmpfs + mdev # 使用 devtmpfs + mdev 管理设备节点
-> [*] Enable root login with password (NEW) # 启用 root 密码登录
-> Root password = 123456 # root 登录密码为 123456
在System configuration 选项中可以配置平台名字,登录密码等信息。
(4)配置Filesystem images
此选项配置我们最终制作的根文件系统为什么格式的,配置如下:
bash
Filesystem images
-> [*] ext2/3/4 root filesystem # 若根文件系统放在 eMMC/SD 卡上,使用 ext3/ext4
-> ext2/3/4 variant = ext4 # 选择 ext4 格式作为根文件系统
-> [*] ubi image containing an ubifs root filesystem # 若使用 NAND Flash,则使用 UBIFS
(5)禁止编译Linux 内核和uboot
buildroot 下载的linux 和uboot官方源码,里面会缺少很多驱动文件,而且最新的linux 内核和uboot 会对编译器版本号有要求,可能导致编译失败。
bash
-> Kernel
-> [ ] Linux Kernel //不要选择编译Linux Kernel 选项!
-> Bootloaders
-> [ ] U-Boot //不要选择编译U-Boot 选项!
(6)配置Target packages
此选项用于配置要选择的第三方库或软件、比如alsa-utils、ffmpeg、iperf 等工具,但是现在我们先不选择第三方库,防止编译不下去!先编译一下最基本的根文件系统,如果没有问题的话再重新配置选择第三方库和软件。
2)编译buildroot
配置完成以后就可以编译buildroot 了,编译完成以后buildroot 就会生成编译出来的根文件系统压缩包,我们可以直接使用。输入如下命令开始编译:
bash
sudo make //注意,一定要加sudo,而且不能通过-jx 来指定多核编译!!!
Ubuntu 中buildroot 源码目录下的dl 文件夹中,dl 文件夹专用用于存放下载下来的源码。
等待编译完成,编译完成以后就会在buildroot-2019.02.6/output/images 下生成根文件系统:

拷贝rootfs.tar并解压后:

3)buildroot 根文件系统测试
测试方法也是通过nfs 挂载的方式,启动uboot,修改bootargs 环境变量。
设置好后启动:(自行创建不存在的目录)

2、buildroot 第三方软件和库的配置
直接在buildroot 里面配置使能,使能后重新编译拷贝即可。
3、buildroot 下的busybox 配置
1)busybox 配置
buildroot 在构建根文件系统的时候也是要用到busybox 的,既然用到了busybox 那么就涉及到busybox 的配置。buildroot 会自动下载busybox 压缩包,buildroot 下载的源码压缩包都存放在/dl 目录下,在dl 目录下就有一个叫做"busybox"的文件夹,此目录下保存着busybox 压缩包:

解压并配置(根据自己的实际需求来配置):
bash
sudo make busybox-menuconfig
2)修改busybox 中文字符的支持
3)编译busybox
配置好以后就可以重新编译buildroot 下的busybox,进入到buildroot 源码目录下,输入如下命令查看当前buildroot 所有配置了的目标软件包,也就是packages:
bash
sudo make show-targets

图中列出了当前buildroot 中所有使能了的packages 包,其中就包括busybox,如果我们想单独编译并安装busybox 的话执行下面命令即可:
bash
sudo make busybox
上述命令就会重新编译busybox。编译完成以后重新编译 buildroot ,主要是对其进行打包,
输入如下命令:
bash
sudo make
重新编译完成以后查看一下output/images 目录下rootfs.tar 的创建时间是否为刚刚编译的,如果不是的话就删除掉rootfs.tar,然后重新执行"sudo make"重新编译一下即可。最后我们使用新的rootfs.tar 启动Linux 系统。
4、根文件系统测试
可能出现的问题:输入命令的时候命令行前面一直都是"#",如果我们进入到某个目录的话前面并不会显示当前目录路径。

解决方法:
打开/etc/profile 文件,找到如下所示内容,然后将其屏蔽掉:
bash
if [ "$PS1" ]; then
if [ "`id -u`" -eq 0 ]; then
export PS1='# '
else
export PS1='$ '
fi
fi
上述代码为原始的设置PS1 环境变量的配置代码,就是它将命令提示符设置为了固定的"#",我们将其屏蔽掉,然后输入如下所示内容:
bash
1 PS1='[\u@\h]:\w$:'
2 export PS1

"PS1" 环境变量用于设置命令提示符格式,格式如下:
bash
PS1 = '命令列表'
/***************************************************/
\! 显示该命令的历史记录编号。
\# 显示当前命令的命令编号。
\$ 显示$符作为提示符,如果用户是root 的话,则显示#号。
\\ 显示反斜杠。
\d 显示当前日期。
\h 显示主机名。
\n 打印新行。
\nnn 显示nnn 的八进制值。
\s 显示当前运行的shell 的名字。
\t 显示当前时间。
\u 显示当前用户的用户名。
\W 显示当前工作目录的名字。
\w 显示当前工作目录的路径
/********************************************************************************************************/
总结区别:
使用buildroot构建根文件系统时配置的busybox和单独使用busybox构建根文件系统时有啥区别?
这是一个非常经典且触及嵌入式Linux构建核心的问题。
简单来说,BusyBox 是"食材",而 Buildroot 是"自动料理机"。
在构建根文件系统(Rootfs)时,BusyBox 只是其中的核心组件(提供 shell 和基础命令),而 Buildroot 是一个完整的构建框架,它不仅包含 BusyBox,还帮你处理了周围所有繁琐的配套工作。
以下是两者的详细对比和核心区别:
1. 核心定位区别
特性 仅使用 BusyBox 构建 使用 Buildroot 构建 本质 一个多合一的二进制工具程序(Tool)。 一个自动化的构建系统(Build System)。 产出物 只有 bin/,sbin/,usr/等基础命令目录。一个完整的、可直接烧录的根文件系统镜像(如 rootfs.ext4)。自动化程度 低(全手动)。你需要自己动手做很多"装修"工作。 高(全自动)。一键生成完整的系统。
2. 构建流程的具体差异
A. 仅使用 BusyBox (手工流)
当你只下载 BusyBox 源码并编译时,你得到的只是一个"裸"的命令集。为了让它成为一个能启动的根文件系统,你必须手动完成以下工作:
创建目录骨架 :手动创建
/dev,/etc,/proc,/sys,/tmp,/var等标准目录。移植动态库 :必须手动去交叉编译工具链中找到对应的 C 库(glibc/uclibc/musl)和加载器(ld-linux.so),并复制到
/lib目录。编写启动脚本 :手动创建
/etc/inittab和/etc/init.d/rcS,配置系统启动后的行为(如挂载文件系统、配置 IP)。配置设备节点 :手动创建
/dev/console,/dev/null,或者配置mdev/eudev。打包镜像 :手动使用
dd,mkfs.ext4等工具将文件夹制作成镜像文件。总结 :灵活但极其繁琐,容易因为漏掉某个
.so库文件导致系统起不来(Kernel Panic)。B. 使用 Buildroot (自动化流)
Buildroot 将 BusyBox 视为一个"软件包"。当你使用 Buildroot 时:
自动配置:Buildroot 会根据你选择的工具链和架构,自动生成最合适的 BusyBox 配置文件(你也可以覆盖它)。
自动补全骨架:它会自动生成符合 FHS 标准的目录结构。
自动处理依赖 :它会自动编译工具链,并将程序运行所需的动态链接库自动拷贝到根文件系统,不需要你操心库文件版本匹配问题。
自动生成脚本:它会自动生成标准的启动脚本(init scripts)。
一键打包 :直接生成
rootfs.tar,rootfs.ext4,rootfs.cpio等格式。
3. 配置方式的区别
BusyBox: 使用
make menuconfig。你只能配置有哪些命令(ls, cp, vi, awk)被编译进去。Buildroot:
层级 1 (Buildroot Config): 使用
make menuconfig。配置目标硬件架构、工具链、内核版本、以及需要安装哪些第三方软件(如 Qt, Python, SSH, SQLite 等)。层级 2 (BusyBox Config): 使用
make busybox-menuconfig。Buildroot 允许你钻进去单独微调 BusyBox 的内部选项。
4. 扩展性(安装第三方软件)
这是两者最大的代沟:
BusyBox 模式 :如果你想在系统中加一个
OpenSSH或者Mosquitto(MQTT),你需要:
去官网下载源码。
手动编写 Makefile 进行交叉编译。
手动解决依赖库(比如 SSH 依赖 OpenSSL/Zlib)。
手动安装到文件系统目录。
Buildroot 模式:
运行
make menuconfig。进入
Target packages->Networking applications。勾选
openssh或mosquitto。运行
make。Buildroot 自动下载、编译、解决依赖、安装。5. 什么时候选哪个?
选择"纯 BusyBox 手工构建"的情况:
学习目的:你想通过从零构建,彻底搞懂 Linux 文件系统的组成结构、启动流程和动态库加载机制。
极简系统:你需要一个极度精简的 Initramfs,大小控制在几百 KB 以内,只需要几个 Shell 命令,不需要任何第三方库。
选择"Buildroot"的情况(推荐):
工程开发:你需要快速产出一个稳定、可复现的固件。
复杂应用:你的系统需要 Qt、Python、WIFI 驱动、音视频库等复杂的第三方软件。
团队协作:Buildroot 的配置文件(defconfig)可以放入 Git 管理,保证团队所有人构建出的镜像是一模一样的。
总结
BusyBox 提供了系统的基本能力(命令)。
Buildroot 提供了系统的完整骨架和生态(包括 BusyBox 本身、库、启动脚本、打包工具)。