###1. 完善init程序
在前面我们通过内核启动参数 “init=/bin/sh”来指定shell作为init程序,在启动后打开一个shell, 但是,我们需要在开机时自动做一些工作, 比如挂载 /proc 和 /sys 文件系统, 这个时候,我们需要一个完整的init程序
默认情况下, bootloader 会传递 “init=/linuxrc” 来指定 “linuxrc” 来作为init进程, 我们可以将 /linuxrc 链接到 /bin/buxybox , 使用 busybox 来作为 init 进程
###2. 使用busybox的init模板
busybox的init程序在运行时,如果存在/etc/inittable文件,Busybox init 程序解析它,然后按照他的指示创建各种子进程,否则使用默认的配置创建子进程.busybox的源码文件的示例带有一份 inittable文件, 我们可以利用这一份现有的模板
sudo mount -o loop initrd.img rootfs
rm rootfs /etc -rf
cp busybox-src/busybox-1.22.1/examples/bootfloppy/etc rootfs/ -r
rootfs/etc -- |
|- fstab
|- inittab
|- profile
|- init.d --
|- rcS
- fstab : 当执行 mount -a 时, 会按照 fstab 里的配置来挂载文件系统
- inittab : busybox init程序开始运行时, 会读取此配置文件执行相应的动作(相当于就是busybox下的init.rc)
- profile : busybox 每开启一个shell时会读取该文件, 因此, 用来设置环境变量
- init.d : d代表demo, 这个目录下一般存放的是服务的的启动脚本, 当执行service xxx 时, 运行 /etc/init.d/xxx
- rcS : busybox的init程序运行的第一个程序(inittab里面定义)
###3. inittab内容详解
inittab中的条目组成如下:
id:runlevels:action:process
- id 字段:在busybox的init程序中, 这个字段有些特别,它用于指定所启动进程的控制tty,如未指定,使用系统console
- runlevels 字段:运行级别,用来指定该条目适用于哪个运行级别。如果该字段为空,代表适用于0—6的运行级别,busybox init程序不支持运行级别
- action 字段:操作
- process 字段:该条目所要执行的进程,可以是任何合法的 shell 命令
run level的定义如下
- halt (Do NOT set initdefault to this)
- Single user mode
- Multiuser, without NFS (The same as 3, if you do not have networking)
- Full multiuser mode
- unused
- X11
- reboot (Do NOT set initdefault to this)
可以在 /etc/inittab 的开头使用initdefault
指定运行级别
id:3:initdefault:
以上条目指定默认的运行级别为3
action字段的定义
- sysinit 指定的进程在访问控制台之前执行,这样的条目仅用于对某些设备的初始化,目的是为了使 init 在这样的设备上向用户提问有关运行级别的问题,init 需要等待进程运行结束后才继续 ,默认执行/etc/init.d/rcS
- respawn 如果 process 字段指定的进程不存在,就启动该进程,init 不会等待处理结束,而是继续扫描 inittab 文件。当该进程被终止时,init 将重新启动它。如果相应的进程已经存在,init 就忽略该条目并继续扫描inittab 文件
- askfirst 类似respawn,不过它的主要用途是减少系统上执行 的终端应用程序的数量。它将会促使init在控制台上显示“Please press Enter to activate this console.”的信息,并在重新启动进程之前等待用户按下Enter键
- wait 启动进程并等待处理结束,处理结束后才去处理下一条条目
- once 启动进程,不会等待处理结束,而是继续处理下一条条目。当该进程被终止时,init 不会重新启动它。从一个运行级别进入另一个运行级别时,如果相应的进程仍在运行,init 就不会重新启动该进程
- ctrlaltdel 当 init 收到信号(SIGNT)时,执行指定进程。用来设置 Ctrl + Alt + Delete 组合键的功能
- shutdown 当系统关机时,执行相应的进程
- restart 当init重新启动时,执行相应的进程。通常此处所执行 的进程就是init本身
###4. busybox init执行流程
BusyBox的 init进程依次进行以下工作:
- 为init设置信号处理进程
- 初始化控制台
- 解析inittab文件(/etc/inittab )
- 执行系统初始化脚本(/etc/init.d/rcS作为缺省)
- 执行所有阻塞的(会导致init暂停的)inittab命令(动作类型:wait)
- 执行所有仅执行一次的inittab命令(动作类型:once)
- 一旦完成以上工作,init进程便会循环执行以下工作:
- 执行所有终止时必须重新启动的inittab命令(动作类型:respawn)
- 执行所有中止时必须重新启动但启动前必须前询问用户的inittab命令(动作类 型:askfirst)
###5. 自定义/etc下的配置
/etc/fstab 内容
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
/etc/inittab 内容
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
/etc/init.d/rcS 内容
#! /bin/sh
/bin/mount -a
如果你还需要做一些其它的初始化工作, 可以在/etc/init.d/rcS中添加
同时, 你需要在rootfs下建立 sys 和 proc 目录
sudo mount -o loop initrd.img rootfs
cd rootfs
mkdir proc
mkdir sys
另外, 还需要建立软连接
ln -s bin/busybox linuxrc
现在,你可以使用使用之前的方法一或者方法二,在qemu中引导内核。现在的系统还很简陋,但是 /proc 文件系统和 /sys 文件系统已经挂在了, 用于内核调试已经足够了。
###6. 使用 mdev
mdev 是 busybox 的 ueventd 实现, 编译 busybox 后, 会生成 bin/mdev
要使用 mdev, 还需要 kernel 支持热插拔, 确保如下的 kernel config 为 “Y”
CONFIG_HOTPLUG=y
CONFIG_NET=y
kernel 运行后, 检查是否存在 “/proc/sys/kernel/hotplug” 文件, 若存在, 则说明支出热插拔
还需要确保 busybox 支持 mdev, 在 busybox 源码中 make menuconfig
, 选中如下的选项
Linux System Utils --->
[*] mdev
[*] Support /etc/mdev.conf
[*] Support subdirs/symlinks
[*] Support regular expressions substitutions when renaming dev
[*] Support command execution at device addition/removal
[*] Support loading of firmwares
编译 busybox 完成后, 修改 根文件系统中的 “/etc/inid.d/rcS”, 在后面添加如下的内容:
mount -t tmpfs mdev /dev
mkdir /dev/pts
mount -t devpts devpts /det/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s