定制外围Linux操作系统

在上一片博文中,我们知道操作系统的启动流程为:

所以启动Linux时有两种启动办法:

  1. 加载bios 的硬件信息-> 读取MBR –>执行Grub ->加载kernel–> 加载驱动–> init –> 执行bash
  2. 加载bios 的硬件信息-> 读取MBR –>执行Grub ->加载kernel–> 加载驱动–> init –> /sbin/init -> 取得run-level信息 -> /etc/rc.d/rc.sysinit -> services –> /etc/rc.d/rc.local –> mingetty –> login

为了从简单入手,我们从手动启动init开始。

1. 获取系统的initrd.img

在上一片博文中,我们已经知道如何解压img文件,此处不在阐述。但是需要说明的是,如果我们需要启动自己的驱动文件,需要更改/boot/grub/grub.conf文件,添加自己的grub启动项。

1
2
3
4
title MicroLinux-Core754		//名称
root (hd0,0) //挂载硬盘
kernel /vmlinuz-2.6.32-754.18.2.el6.x86_64
initrd /microlinux.img

现在我们讲讲如何制作自己的img文件。

首先,我们需要写一个脚本init作为启动项,使得内核用该文件系统启动后能够直接获得一个Bash

1
2
#!/bin/bash
/bin/bash

​ 为了启动我们的init文件,我们还需要binlib64文件,其中bin文件夹存放可执行文件,lib64存放应用程序所依赖的动态库。由于bash等这些命令需要依赖/lib64等目录下的一些动态链接的共享库,所以需要将依赖的库拷贝到小系统对应的目录下,用ldd命令查询应用程序及其依赖的动态库。完成之后,执行:

1
find . | cpio -H newc -o | gzip > /boot/microlinux.img

将根文件系统打包成microlinux.img放到/boot目录下。启动时系统会自动执行microlinux.img中的init(配置好grub后)

此时我们重启系统,并在grub引导程序选择我们自己的启动程序后,就可以进入bash(虽然啥都没有)

2. 挂载原有系统

如果想挂载原系统,需要加载一些驱动程序,比如ext4文件系统的驱动、scsi设备的相关驱动等。这些驱动程序

在系统的/lib/modules里,我们需要将一些必要的驱动拷到我们的小系统里,并需要modinfolsmod命令

编写init文件为

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
#!/bin/bash
# mount some important things
export PATH=/sbin:/bin:/usr/sbin:/usr/bin

mknod /dev/null c 1 3
mknod /dev/console c 5 1
insmod /lib/modules/kernel/drivers/scsi/scsi_transport_spi.ko
insmod /lib/modules/kernel/drivers/message/fusion/mptbase.ko
insmod /lib/modules/kernel/drivers/message/fusion/mptscsih.ko
insmod /lib/modules/kernel/drivers/message/fusion/mptspi.ko
insmod /lib/modules/kernel/lib/crc-t10dif.ko
insmod /lib/modules/kernel/drivers/scsi/sd_mod.ko

insmod /lib/modules/kernel/fs/mbcache.ko
insmod /lib/modules/kernel/fs/jbd2/jbd2.ko
insmod /lib/modules/kernel/fs/ext4/ext4.ko

mount -t proc proc /proc >/dev/null 2>&1
mount -t sysfs sysfs /sys >/dev/null 2>&1

mkdir root
mknod /dev/sda2 b 8 2
mount -t ext4 /dev/sda2 /root/

exec /bin/bash

这样,系统在启动时会先加载init中所写的一些ko文件,再挂载系统磁盘,然后挂载原有系统。

3. 完成拥有管理设备能力(udev)

udevd是管理、监控主机设备的服务程序,有了udevd后可以自动加载所需的驱动模块,避免了手动挂载的麻烦。udev依赖于sysfs系统文件,udevd的规则文件在/lib/udev/目录下,配置文件在/etc/udev/目录下,同时还需要/etc/nsswitch.conf配置的名称服务交换,其依赖的库为/lib目录下以libnss开头的文件,将上述文件拷贝到我们的目录下,然后使用/sbin/start_udev命令可以启动udevd服务

启动后,udev会自动在/dev目录下创建设备节点,普通linux环境/dev目录是tmpfs虚拟磁盘文件系统,设备节点可隶属于不同的用户和组(由/etc/group/etc/passwd指派uidgid)。

这样我们的init文件可写为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
# mount some important things
export PATH=/sbin:/bin:/usr/sbin:/usr/bin

mknod /dev/null c 1 3
mknod /dev/console c 5 1

mount -t proc proc /proc >/dev/null 2>&1
mount -t sysfs sysfs /sys >/dev/null 2>&1

/sbin/start_udev

mkdir root
mknod /dev/sda2 b 8 2 #这个2代表硬盘符,sda1就是 b 8 1
mount -t ext4 /dev/sda2 /root/

exec /bin/bash

我们看看现在小系统里都有什么

其中/root为创建的用户目录,/proc/sys为系统目录,/dev为设备目录,/var存放变量,以上目录在小系统的img文件中均为空文件夹,lib64存放共享库文件,lib文件存放kernel的modules驱动目录和udevd的规则文件.

4.完成拥有login登录能力(多窗口)

login有一套复杂的机制,我们不妨先实现能在bash中手动执行login创建多窗口。

login的依赖很复杂,它有一套完整的认证体系,如/etc/pam.d的配置和/lib64/security的依赖(这个共享库还会依赖别的共享库,需要注意)login还涉及用户组管理/bin/chgrp/bin/chown/bin/chmod等,保存用户名的文件/etc/passwd/etc/group,用户密码文件为/etc/shadow等等

我们可以用strace命令去追踪login执行时所调用的文件,并将他们拷贝到我们的小系统里,这些文件在/etc,/lib,/lib64中都有分布。

那怎么知道我们可以使用多窗口登入了呢,使用udevd管理设备后,我们可以在打开小系统后发现里面有一堆tty文件,意味着我们可以同时使用多个终端,但是我们发现使用ctrl+alt+Fn组合键并不能打开新的终端,这是因为快捷键驱动我们还没挂起,但是没关系,我们可以将login重定向到另一个终端,在bash中执行

1
login > /dev/tty2  2>/dev/tty2 < /dev/tty2 &

就可以在后台执行一个login,并重定向到tty2,现在在使用ctrl+alt+F2就可以打开tty2终端。我们使用ps命令查看进程,就可以发现一些运行的命令是在tty2下运行的。

5. 使用/sbin/init管理的小系统原型

在以往的环节中,我们都是手动启动设备管理,手动登入和执行bash,但是如果我们退出这个bash,系统就会死掉,所以我们需要一个可以保护系统bash和控制启动流程的程序,sbin/init就很好的帮我们解决了这些烦恼。

执行/sbin/init后,执行顺序如下:

/sbin/init的过程大致分为三块:第一块是udevd加载驱动模块、文件系统检查和根切换,相关配置在/etc/rc.sysinit中;第二块是启动各项服务,相关配置在/etc/rc.d/目录下;第三块是登录部分,需要调用/sbin/mingetty/bin/login等命令。将上述所涉及的命令及文件拷贝到小系统对应的目录下,并对配置进行修改。

我们需要把与init启动相关的文件拷到小系统里

1
2
3
4
5
6
7
8
/etc/init/rcS.conf 加载rc.sysinit脚本,完成系统初始化任务
/etc/init/rc.conf 兼容脚本,负责各运行级别的调用处理
/etc/init/rcS-sulogin.conf 为单用户模式启动/sbin/sushell环境
/etc/init/control-alt-delete.conf 控制终端下的Ctrl+Alt+Del热键操作
/etc/init/start-ttys.conf 配置tty终端的开启数量、设备文件
/etc/sysconfig/init 控制tty终端的开启数量、终端颜色方案
/etc/init/tty.conf 控制tty终端的开启
/etc/inittab 控制启动优先级

配置好/sbin/init后,我们可以在init脚本中直接调用sbin/init,便可完成系统的启动