<linux> busybox制作rootfs

Ⅰ、下载busybox

本次实验在ubuntu环境下进行。

busybox的官方链接https://busybox.net/

找到某个版本的busybox,点击git
在这里插入图片描述
在这里插入图片描述
使用git clone将busybox源码复制到Ubuntu下
在这里插入图片描述


Ⅱ、修改busybox支持中文字符

默认的busybox源码,是不支持中文字符显示的
需要修改以下几个文件

1、libbb/printable_string.c文件

显示中文,需要解除对0x7f字符数值的限制,原来的代码是将超过0x7f的字符,重新赋值为?,所以看到的中文字符,全部会显示为?

const char* FAST_FUNC printable_string2(uni_stat_t *stats, const char *str)
{
        char *dst;
        const char *s;

        s = str;
        while (1) {
                unsigned char c = *s;
                if (c == '\0') {
                        /* 99+% of inputs do not need conversion */
                        if (stats) {
                                stats->byte_count = (s - str);
                                stats->unicode_count = (s - str);
                                stats->unicode_width = (s - str);
                        }
                        return str;
                }
                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) 
                                *d = '?';
*/
                        if (c < ' ') //解除中文字符限制
                                *d = '?';
                        d++;
                }
                if (stats) {
                        stats->byte_count = (d - dst);
                        stats->unicode_count = (d - dst);
                        stats->unicode_width = (d - dst);
                }
        }
#endif
        return auto_string(dst);
}

2、libbb/unicode.c文件
unicode_conv_to_printable2函数下,解除对数值大于0x7f字符的限制。

//截取部分代码
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);
                        while ((int)--width >= 0) {
                                unsigned char c = *src;
                                if (c == '\0') {
                                        do
                                                *d++ = ' ';
                                        while ((int)--width >= 0);
                                        break;
                                }
//                              *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++;
                        }
                }
                if (stats) {
                        stats->byte_count = (d - dst);
                        stats->unicode_count = (d - dst);
                        stats->unicode_width = (d - dst);
                }
                return dst;
        }

        dst = NULL;
        uni_count = uni_width = 0;
        dst_len = 0;
        while (1) {


Ⅲ、选择配置

busybox默认支持三种自动配置方式

  1. 默认配置
make defconfig
  1. 所有配置
make allyesconfig
  1. 最小配置
make allnoconfig

一般情况下,我们使用默认配置

当然也可以选择手动配置,与大部分的linux SDK一样,支持menuconfig图形配置界面

make menuconfig

在这里插入图片描述
可以通过配置选项来选择/取消一些应用工具。
比如linux下常见的编辑器,vi,可以通过Editors–>vi来选择/取消
在这里插入图片描述

基于defconfig上选择一些基本配置

1. 选择动态库编译busybox

取消选择静态库编译,即选择动态库, 静态库会有较大的文件生成。

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

在这里插入图片描述

2. 选择vi支持命令行

Settings --->
		vi-style line editing commands

在这里插入图片描述

3.取消simplified modutils

不知为何,大家都这么做,疑问暂时保存在这里。

Linux Module Utilities  --->
 	Simplified modutils

在这里插入图片描述

4. 选择mdev

支持mdev相关的内容,默认配置下,已经勾选。

Linux System Utilities  --->
	mdev
	Support /etc/mdev.conf
	...
	...
	Support daemon mode

在这里插入图片描述

5.选择unicode,支持中文

Settings --->
	[*] Support Unicode
	[*]    Check $LC_ALL, $LC_CTYPE and $LANG environment variables

在这里插入图片描述


Ⅳ、交叉编译工具配置

与uboot、linux kernel的编译类似,不同cpu架构下的rootfs需要对应的工具链来编译,才可以适用到对应架构下

可以通过修改busybox目录下的Makefile,指定编译工具链和架构。
以编译arm架构为例

vim Makefile

若没有指定arch,makefile会自带获取uname中的架构名称。也可以手动指定ARCH ?= arm

# 工具链需要对应ubuntu环境下安装的工具链,如果没有对应工具链,需要先安装工具链
CROSS_COMPILE ?= arm-linux-gnueabihf- 
ifneq ($(CROSS_COMPILE),)
SUBARCH := $(shell echo $(CROSS_COMPILE) | cut -d- -f1 | sed 's:^.*/::g')
else
SUBARCH := $(shell uname -m)
endif
SUBARCH := $(shell echo $(SUBARCH) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
                                         -e s/arm.*/arm/ -e s/sa110/arm/ \
                                         -e s/s390x/s390/ -e s/parisc64/parisc/ \
                                         -e s/ppc.*/powerpc/ -e s/mips.*/mips/ )

ARCH ?= $(SUBARCH)

或者在编译的时候,传入指定工具链和架构

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 

Ⅴ、编译busybox,输出到指定文件夹

测试rootfs,可以将开发板上的rootfs通过nfs挂载到ubuntu上,这样可以方便修改,不用重新制作镜像,烧录。

busybox编译,支持将编译的产物输出到指定的目录下。
CONFIG_PREFIX=<路径>的形式,需要先创建输出文件夹。
使用make install进行编译安装

make install ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- CONFIG_PREFIX=/home/xxx/rootfs/

编译完成后,可以看到rootfs目录下的文件,具备了基本的rootfs文件夹

ls /home/xxx/rootfs/

bin linuxrc  sbin  usr

还需要添加一些c库,因为编译rootfs的时候选择了非静态编译。

C库是从交叉编译工具链中复制而来

# 首先创建 lib
mkdir lib

# 复制交叉编译工具链中的C库到lib目录下,工具链下的libc/lib/*so*, *.a, -d表示带符号拷贝
cp /opt/gcc-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/*so* /opt/gcc-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/*.a ./lib/ -d

# 复制工具链下的lib/*so* ,*.a 到rootfs的lib/
cp /opt/gcc-arm-linux-gnueabihf/arm-linux-gnueabihf/lib/*.so /opt/gcc-arm-linux-gnueabihf/lib/*.a ./lib -d

# 删除linux armhf.so,是个软连接文件,需要复制实际文件到rootfs目录下
ls -l ld-linux-armhf.so.3
ld-linux-armhf.so.3 -> ld-2.19-2014.08-1-git.so* # 实际上是个软连接

#拷贝真实的ld-linux-armhf.so.3, 暂时不知道为何
rm ld-linux-armhf.so.3
cp /opt/gcc-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/ld-2.19-2014.08-1-git.so ld-linux-armhf.so.3

# 最后拷贝工具链的/usr/lib/*.so ,*.a到rootfs的/usr/lib目录下
mkdir usr/lib	
cp /opt/gcc-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib/*.so /opt/gcc-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib/*.a ./usr/lib/ -d

到这里rootfs就准备完成,查看一下rootfs的大小,总共127M,太大了

du -sh rootfs

4.0K    rootfs/sbin
724K    rootfs/bin
60M     rootfs/lib
4.0K    rootfs/usr/sbin
4.0K    rootfs/usr/bin
67M     rootfs/usr/lib
67M     rootfs/usr
127M    rootfs/

还需要准备一些空文件夹,给rootfs挂载后使用

mkdir dev # 用于给kernel挂载设备
mkdir etc # 用于装载一些配置
mkdir tmp # 用于装载临时生成的文件
mkdir proc # 用于进程相关存储
mkdir sys # 用于device、class等驱动创建节点目录

Ⅵ、uboot配置nfs挂载rootfs

进入uboot,配置rootfs,通过nfs挂载rootfs。
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>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
  • server-ip:服务器 IP 地址,也就是存放根文件系统主机的 IP 地址,那就是 Ubuntu 的 IP
    地址,比如我的 Ubuntu 主机 IP 地址为 192.168.31.90
  • root-dir:根文件系统的存放路径,比如我的就是/home/allen/Desktop/rootfs/
  • nfs-options:NFS 的其他可选选项,一般不设置。
  • client-ip:客户端 IP 地址,也就是我们开发板的 IP 地址,Linux 内核启动以后就会使用
    此 IP 地址来配置开发板。此地址一定要和 Ubuntu 主机在同一个网段内,并且没有被其他的设
    备使用,在 Ubuntu 中使用 ping 命令 ping 一下就知道要设置的 IP 地址有没有被使用,如果不能
    ping 通就说明没有被使用,那么就可以设置为开发板的 IP 地址,比如我就可以设置为
    192.168.1.248。
  • 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 //保存环境变量

ubuntu配置nfs

安装nfs

sudo apt-get install nfs-kernel-server

配置nfs目录

vim /etc/exports


# /etc/exports: the access control list for filesystems which may be exported
#               to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)
#
/home/allen/Desktop/  *(rw,sync,no_root_squash) # 这里设置/home/allen/Desktop/作为nfs共享目录。

(rw,sync,no_root_squash)设置rw权限。

测试mount是否成功

showmount -e 

Export list for allen:
/home/allen/Desktop *

windows尝试挂载nfs
打开电脑cmd

mount 192.168.xx.90:/home/allen/Desktop

挂载成功
在这里插入图片描述
打开我的电脑可以看到
在这里插入图片描述
到此nfs已经挂载成功,就可以尝试在uboot中通过nfs来挂载rootfs了。

uboot nfs挂载rootfs

设置bootargs,roofs属性


setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.31.90:/home/allen/Desktop/rootfs,nfsvers=3,proto=tcp ip=192.168.31.25:192.168.31.90:192.168.31.1:255.255.255.0::eth0:off'
  • root=/dev/nfs ⇒ 指定roofs启动方式是nfs
  • nfsroot=192.168.31.90:/home/allen/Desktop/rootfs,nfsvers=3,proto=tcp 。

亿个小细节:
    重点是nfsvers=3,ubuntu20.04,如果不指定nfsvers=3会出现无法连接到nfs的问题。

启动内核检查是否挂载rootfs成功。

[    3.604707] fec 20b4000.ethernet eth0: Freescale FEC PHY driver [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=20b4000.ethernet:01, irq=-1)
[    3.644131] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[    6.684452] fec 20b4000.ethernet eth0: Link is Up - 100Mbps/Full - flow control rx/tx
[    6.704091] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
[    6.724370] IP-Config: Complete:
[    6.727636]      device=eth0, hwaddr=40:a9:22:34:68:68, ipaddr=192.168.31.25, mask=255.255.255.0, gw=192.168.31.1
[    6.738042]      host=192.168.31.25, domain=, nis-domain=(none)
[    6.744063]      bootserver=192.168.31.90, rootserver=192.168.31.90, rootpath=
[    6.751343] gpio_dvfs: disabling
[    6.754661] can-3v3: disabling
[    6.757988] ALSA device list:
[    6.760971]   #0: wm8960-audio
[    6.805225] VFS: Mounted root (nfs filesystem) readonly on device 0:14.
[    6.814381] devtmpfs: mounted
[    6.818079] Freeing unused kernel memory: 532K (80b43000 - 80bc8000)
can't run '/etc/init.d/rcS': No such file or directory

可以看到log上获取到了网络, IP地址设置正常,看到logVFS: Mounted root (nfs filesystem) readonly on device 0:14.说明rootfs挂载正常。

Ⅶ、处理linux启动机制

1、linuxrc与/etc/init.d/rcS

rootfs挂载后,会执行linuxrc

drwxrwxr-x    2 1000     1000          4096 May 16  2022 bin
drwxr-xr-x    6 0        0             2980 Jan  1 00:00 dev
drwxrwxr-x    2 1000     1000          4096 May 16  2022 etc
drwxrwxr-x    2 1000     1000          4096 May 16  2022 lib
lrwxrwxrwx    1 1000     1000            11 May 16  2022 linuxrc -> bin/busybox
drwxrwxr-x    2 1000     1000          4096 May 16  2022 proc
drwxrwxr-x    2 1000     1000          4096 May 16  2022 sbin
drwxrwxr-x    2 1000     1000          4096 May 16  2022 tmp
drwxrwxr-x    5 1000     1000          4096 May 16  2022 usr

可以看到linuxrc -> bin/busybox,会执行/etc/inittab脚本,然后在调用/etc/init.d/rcS文件配置一些基本的shell变量环境

Created with Raphaël 2.3.0 rootfs挂载成功 启动/etc/inittab 启动/etc/init.d/rcS 文件rcS存在? 系统启动 '/etc/init.d/rcS': No such file or directory yes no

/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 文档。

2、创建/etc/fstab

/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

<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。

配置后,重新启动linux,报错就没有了

[    6.737030]      device=eth0, hwaddr=40:a9:22:34:68:68, ipaddr=192.168.31.25, mask=255.255.255.0, gw=192.168.31.1
[    6.747440]      host=192.168.31.25, domain=, nis-domain=(none)
[    6.753457]      bootserver=192.168.31.90, rootserver=192.168.31.90, rootpath=
[    6.760738] gpio_dvfs: disabling
[    6.764056] can-3v3: disabling
[    6.767381] ALSA device list:
[    6.770363]   #0: wm8960-audio
[    6.819245] VFS: Mounted root (nfs filesystem) readonly on device 0:14.
[    6.834257] devtmpfs: mounted
[    6.837956] Freeing unused kernel memory: 532K (80b43000 - 80bc8000)

ls /sys也可以看到驱动device都挂载到sys下

/ # ls sys/
block/     class/     devices/   fs/        kernel/    power/
bus/       dev/       firmware/  fsl_otp/   module/

/proc目录下进程也正常。

/ # cat /proc/
1/             5/             87/            driver/        net/
10/            6/             88/            execdomains    pagetypeinfo
100/           61/            89/            fb             partitions
101/           62/            9/             filesystems    self/
102/           67/            90/            fs/            softirqs
11/            68/            91/            interrupts     stat
12/            69/            92/            iomem          swaps
13/            7/             93/            ioports        sys/
14/            70/            99/            irq/           sysrq-trigger
15/            71/            asound/        kallsyms       sysvipc/
16/            72/            buddyinfo      key-users      thread-self/
17/            73/            bus/           keys           timer_list
18/            74/            cgroups        kmsg           tty/
19/            75/            cmdline        kpagecount     uptime
2/             76/            config.gz      kpageflags     version
20/            77/            consoles       loadavg        vmallocinfo
21/            8/             cpu/           locks          vmstat
22/            82/            cpuinfo        meminfo        zoneinfo
23/            83/            crypto         misc
24/            84/            device-tree/   modules
3/             85/            devices        mounts
4/             86/            diskstats      mtd

3、/etc/inittab机制

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

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

简单来说,inittab可以理解为预设一些指令,执行指令时进而执行一些动作。

四个选项的说明如下:

<id>:	每个指令的标识符,不能重复。但是对于 busybox 的 init 来说,<id>有着特殊意义。
对于 busybox 而言<id>用来指定启动进程的控制 tty,一般我们将串口或者 LCD 屏幕设置为控
制 tty。
<runlevels>:对 busybox 来说此项完全没用,所以空着。
<action>:动作,用于指定<process>可能用到的动作。
<process>:具体的动作,比如程序、脚本或命令等。
action作用
sysint系统启动完成后,开始初始化,对应sysint的process 会执行一次
respawn当respawn对应的process,终止后,会立刻重新执行一次process ,与openwrt的procd守护进程配置类似,也有respawn配置
askfirst和 respawn 类似,在运行 process 之前在控制台上显示“Please press Enter to activate this console.”。只要用户按下“Enter”键以后才会执行 process。
wait在init过程,要等wait对应的process执行完成后,才继续init
once仅执行一次,而且不会等待 process 执行完成
restart当执行重启的时候才会执行 process
ctrlaltdel键盘按下ctrl + alt + del组合键,触发process执行
shutdown系统关机时,执行对应的process

/etc/inittab 示例:

::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r 7 ::shutdown:/sbin/swapoff -a

当执行shutdown时,就会执行命令/bin/umount -a -r 7 ::shutdown:/sbin/swapoff -a

至此整个rootfs就完成!完结撒花。


总结

1、rootfs是linux重要的一环,可以通过nfs挂载,或直接在存储空间挂载。

2、rootfs启动后,需要经历几个阶段,linuxrc、/etc/inittab、/etc/init.d/rcS、/etc/fstab。需要了解三个文件的作用,inittab保存一些<动作:执行>指令的绑定,rcS启动后自动执行,配置一些环境变量,或添加自定义启动程序,类似/etc/rc.local。 fstab是配置基本的文件系统、tmpfs、proc、sysfs等

3、制作rootfs可以通过busybox,或直接用buildroot制作。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值