Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3759891
  • 博文数量: 880
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 6155
  • 用 户 组: 普通用户
  • 注册时间: 2016-11-11 09:12
个人简介

To be a better coder

文章分类

全部博文(880)

文章存档

2022年(5)

2021年(60)

2020年(175)

2019年(207)

2018年(210)

2017年(142)

2016年(81)

分类: LINUX

2020-05-13 17:22:21

https://blog.csdn.net/xiaoshixiu/java/article/details/86512791

busybox的正常运行,需要一些必须的配置项,最不可缺少的有两个:

1)/etc/init.d/rcS,/sbin/init成功运行之后,会执行该脚本,我们可以在该脚本中执行后续的初始化操作,如mount文件系统、创建设备节点、等等。

2)/etc/inittab,/etc/init.d/rcS成功执行后,系统就已经处于ready状态,init进程会读取inittab中的配置,决定在哪个设备上启用控制台(当然不止这些)。

https://blog.csdn.net/xiaoshixiu/article/details/86512791

busybox制作initrd.img和根文件系统
参考书籍:《深度探索Linux操作系统 系统构建和原理解析》
参考博客:https://blog.csdn.net/mao0514/article/details/51248738

(一)开发环境介绍
1.使用win7_64的笔记本安装Virtualbox虚拟机,笔记本cpu为i5-2450m。虚拟机上安装Ubuntu16.04系统作为编译环境,同时虚拟机也作为最小系统运行环境。
2.最小系统使用Linux内核版本选择4.15.0,内核配置使用x86_64默认配置(即使用命令make x86_64_defconfig)。busybox选择版本1.30.0,使用busybox制作initrd.img,并且同使用busybox制作根文件系统,busybox编译配置相同,只需要在默认配置上修改为静态链接(setting–Build static binary )。
3.在根目录新建文件夹/vita作为新系统启动分区,使用gparted分配10G(大小随便)给新分区,新分区设备文件为/dev/sda3。将新分区挂载在/vita目录,在此目录下新建/boot用于存放bzImage和initrd.img。同时/vita也存放根文件系统。制作好的目录如下:


(二)制作initrd.img
1.下载busybox包,解压,配置,编译,安装等步骤之后,会在当前目录下生产_install目录,里面有一些不依赖其他库的静态进程,可以直接运行。

我们首先将_install拷贝到initramfs目录用于生成initrd.img

mkdir initramfs
cp -R busybox-1.30.0/_install/* initramfs/
cd initramfs
1
2
3
2.测试initrd。由于内核将initrd.img镜像解压到根文件系统后就将运行权交于initrd.img中的/init程序,所以我们可以自行选择是否要加载根文件系统,可以不加载根文件系统直接使用initrd.img里的内容做根文件系统,下面进行两种测试,单独运行initrd.img。
(1)测试一:使用linuxrc的内容作为启动脚本
① 由于我们使用initramfs,所以内核运行时是找init文件而不是linuxrc,所以直接将linuxrc改名为init

mv linuxrc init
1
这里可以稍微解释下linuxrc的运行原理,在busybox的源码下/init/init.c有如下内容:

表明linuxrc的作用和init一样,也就是执行linuxrc就是执行/sbin/init。我们知道busybox使用软链接将一个进程链接成多个进程,然后通过main函数args的第一个参数(也就是进程名)来区分具体执行哪一个,我们直接把文件名该为init其实就相当于执行了/sbin/init程序。
② 新建文件夹,添加配置文件

mkdir dev etc lib mnt proc sys
1
配置文件可以选择busybox里面的例子,在busybox-1.30.0\examples\bootfloppy\etc目录下,可以找到相关文件

将这些文件拷贝到initramfs/etc下,于是etc结构如下。


cp -R ../busybox-1.30.0/examples/bootfloppy/etc/* etc/
1
之所以要添加配置文件是因为/init程序等同于/sbin/init,了解linux system V启动方式的同学肯定知道,/sbin/bin会根据/etc/inittab的配置内容进行相关配置,如果这里不进行配置运行内核时会首先提示找不到rcS文件,也即“can’t run ‘/etc/init.d/rcS’: No such file or directory”。
这里稍微解释下各个文件内容

etc/inittab

::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
tty2::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
1
2
3
4
简单说就是每一行表示一种执行策略,例如第一行表示在系统启动时执行RCS,第二行表示如果sh挂了会自动重启sh,第三行表示使用tty2启动的sh启动时会首先询问,第四行表示使用ctrl+alt+del快捷键时执行的程序。这里我们改下配置内容

::sysinit:/etc/init.d/rcS
console::respawn:-/bin/sh
1
2
将sh启动的控制程序交给console,并且删除其他操作。

etc/fstab

proc        /proc    proc    defaults    0    0
1
fstab表示系统启动时默认挂载的文件系统,通常可以把一些特殊文件系统挂载下,例如/proc,/sysfs

etc/profile

# /etc/profile: system-wide .profile file for the Bourne shells

echo
echo -n "Processing /etc/profile... "

# no-op
echo "Done"
echo
1
2
3
4
5
6
7
8
profile通常是使用tty并进行登录时才会用到,profile是通常会调用~/.bashrc的配置修改用户环境变量。

etc/init.d/rcS

#! /bin/sh

/bin/mount -a
1
2
3
rcS在这里是作为inittab的启动脚本

③ 添加控制台设备文件
内核在准备好initramfs后会判断/dev/console是否存在,不存在就报错,存在就会继续执行/init并将以后的输出输出到控制台中。


mknod dev/console c 5 1
mknod dev/null c 1 3
1
2
这里我进行了删除测试,发现即使没有新建这两个设备结点也能正常运行,在查阅《深度探索Linux操作系统》后,发现里面有解答,除了我们自己创建的initrd.img外,内核会自己创建一个内置的initramfs文件系统,并在内置initramfs中执行命令创建console设备结点。所以添加设备结点这一步我们可以省略。

④ 配置grub,在/boot/grub.cfg中先新建菜单项,可以参考我的上一篇博客“Linux内核学习(3)最小系统制作”,再配置initrd的参数,表示initrd.img的位置,例如:

⑤ 生成initrd.img文件,并将此文件拷贝到/boot下,最后再重启

find . |cpio -o -H newc|gzip ../initrd3.img
cp ../initrd3.img /vita/boot
1
2
⑥ 在grub菜单中选择自己新建的菜单

启动后结果如下,执行下ls,发现只有initrd.img里面的内容,而不是根文件系统/vita下的内容


(2)测试二:自定义/init脚本
① 可以直接在上述基础上进行,也就是修改/init的内容。

rm init
vim init
1
2
init:

#!/bin/sh
#
echo "exec initramfs init"
echo "mounting proc and sys"

mount -t proc proc /proc
mount -t sysfs sysfs /sys

echo "detect and export hardware info"
mdev -s


echo "start /bin/sh"
exec /bin/sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
这里注意不能直接执行/bin/sh,需要先先挂载proc和sysfs,然后创建设备结点,否则会造成如下错误,表示init执行错误。mdev的作用是将sysfs文件系统下扫描的设备在/dev下创建设备结点。


② 修改init后的步骤和上述“测试一”一样,重启后显示如下

结果虽然执行了sh,但是仍有提示“can’t access tty”,而且部分busybox功能失效,例如执行reboot无任何反映,应该是未执行相关初始化的原因。但是我们这里只是测试,所以无需纠结。
3.使用initrd启动根文件系统
(1)当initrd中充当挂载根文件系统的桥梁时,这时initramfs/etc下可以不需要配置(这里我们依旧保留,其实没有使用),而应当把配置文件移动到根文件系统中。
(2)修改initramfs/init启动脚本

vim init
1
init:

#!/bin/sh
#
echo "exec initramfs init"
echo "mounting proc and sys"

mount -t proc proc /proc
mount -t sysfs sysfs /sys

echo "detect and export hardware info"
mdev -s

echo "Mount real rootfs to /mnt/sysroot..."
mount -t ext4 /dev/sda3 /mnt/sysroot

echo "Switch to read rootfs..."
exec switch_root /mnt/sysroot /sbin/init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
这里我们多执行了两步操作,挂载根文件系统/dev/sda3到/mnt/sysroot目录,执行switch_root切换根文件系统目录到/mnt/sysroot下,并且执行/sbin/init。

(三)制作根文件系统
1.制作根文件系统和initrd.img步骤相差无几,首先是一样的拷贝基本文件。

cp -R ../busybox-1.30.0/_install/* /vita
1
2.配置文件

cp -R etc/* /vita/etc/
1
3.创建相关文件,删除无用文件

cd /vita
mkdir -p dev etc lib mnt/sysroot proc sys
rm linuxrc
1
2
3
4.修改下fstab和rcS,使用fstab添加sysfs的挂载,使用rcS自动创建设备结点

vim etc/fstab
1
fstab:

proc        /proc    proc    defaults    0    0
sysfs        /sys    sysfs    defaults    0 0
1
2
修改rcS:

vim etc/init.d/rcS
1
rcS:

#!/bin/sh

/bin/mount -a
mdev -s
1
2
3
4
5.重启

可以看到log都正确的打印,输入ls后看到的目录的确是/vita目录,也就是正确切换到根文件系统了。

(四)常见错误
1.“Failed to create /dev/root:”,这种错误有时候会被误解为驱动问题,其实查看kernel源码会发现有如下调用顺序:

可以看到,如果init文件不存在(没指定参数时默认启动init),内核会主动挂载根文件系统,但是如果没有进行任何先前动作就去挂载一般都会失败,例如需要先进行挂载/proc和/sysfs。所以如果我们忘记在initrd.img中写/init启动脚本会出现这种错误。
2.“can’t run ‘/etc/init.d/rcS’: No such file or directory”,这种错误比较常见,但是经常不那么容易弄懂。

我这里说一种我出现的问题,花了比较长的时间才解决。
首先我的根文件系统明明已经写了rcS而且也没有任何乱码,仔细检查根文件系统明明没有任何问题。这时我突然想到,如果在initrd.img/init脚本中无意间执行了initrd.img中的/sin/init程序,那么此程序会检查有没有initrd.img/etc/inittab配置,如果没有此配置文件,会默认执行initrd.img/etc/init.d/rcS脚本,所以如果initrd.img/etc下什么文件都没有,那么首先报的问题就是缺少initrd.img/etc/init.d/rcS。但是后来我仔细检查initrd.img/init内容发现并没有执行到initrd.img/sbin/init。虽然没有解决问题,但是我却发现了另外一个问题,initrd.img/init文件没有执行权限,也就是如下:

可以看到init文件的权限没有可执行权限,但是这个问题会影响到rcS吗,我又研究了一下kernel的源码,发现有如下片段:

if (ramdisk_execute_command) {
        ret = run_init_process(ramdisk_execute_command);
        if (!ret)
            return 0;
        pr_err("Failed to execute %s (error %d)\n",
               ramdisk_execute_command, ret);
    }

    /*
     * We try each of these until one succeeds.
     *
     * The Bourne shell can be used instead of init if we are
     * trying to recover a really broken machine.
     */
    if (execute_command) {
        ret = run_init_process(execute_command);
        if (!ret)
            return 0;
        panic("Requested init %s failed (error %d).",
              execute_command, ret);
    }
    if (!try_to_run_init_process("/sbin/init") ||
        !try_to_run_init_process("/etc/init") ||
        !try_to_run_init_process("/bin/init") ||
        !try_to_run_init_process("/bin/sh"))
        return 0;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ramdisk_execute_command变量在默认情况下是/init,这段代码就是当/init执行不成功时会继续执行/sbin/init,而这时就会执行到上述initrd.img/sbin/init中,所以就会出现上述错误。
明白错误原理后只需要修改init权限,给所有用户添加执行:

chmod a+x init
1
重新重启后就正常了,这个问题其实不难解决,难的是报的问题非常奇怪不容易弄懂原因。
————————————————
版权声明:本文为CSDN博主「上天肖」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaoshixiu/java/article/details/86512791

阅读(1318) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~