###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的定义如下

  1. halt (Do NOT set initdefault to this)
  2. Single user mode
  3. Multiuser, without NFS (The same as 3, if you do not have networking)
  4. Full multiuser mode
  5. unused
  6. X11
  7. 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进程依次进行以下工作:

  1. 为init设置信号处理进程
  2. 初始化控制台
  3. 解析inittab文件(/etc/inittab )
  4. 执行系统初始化脚本(/etc/init.d/rcS作为缺省)
  5. 执行所有阻塞的(会导致init暂停的)inittab命令(动作类型:wait)
  6. 执行所有仅执行一次的inittab命令(动作类型:once)
  7. 一旦完成以上工作,init进程便会循环执行以下工作:
  8. 执行所有终止时必须重新启动的inittab命令(动作类型:respawn)
  9. 执行所有中止时必须重新启动但启动前必须前询问用户的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