> 本文由云+社区发表
> 作者:我是乖宝宝哦
一般来说,我们所说的`Linux系统`指的是各种基于`Linux Kernel`和`GNU Project`的操作系统发行版。为了掌握`Linux`操作系统的使用,了解 `Linux`操作系统的运作过程,理解内核与外围支撑系统的关系,加深对开源操作系统的认识,我决定造个轮子——自己定制一个`Linux`文件系统。
这里有两种实现方法:
- 直接自己实现`init**\*(M1)***`
> 加载bios 的硬件信息-> 读取MBR –>执行Grub ->加载kernel–> 加载驱动–> init –> 执行bash
- 利用系统`/sbin/init**\*(M2)***`
> 加载bios 的硬件信息-> 读取MBR –>执行Grub ->加载kernel–> 加载驱动–> init –> /sbin/init -> 取得run-level信息 -> /etc/rc.d/rc.sysinit -> services –> /etc/rc.d/rc.local –> mingetty –> login
我们先选择**\*M1***。
## 思路
1. 利用原有系统复制必备部件到新存储器
2. 利用`initrd.img`机制在`RAM Disk`中测试
3. 搭配原文件内核和模块启动
## Step1:**获得shell版本的initrd.img**
首先,我们可以写一个脚本`init`,使得内核用该文件系统启动后能够直接获得一个`Bash`。
![img](https://ask.qcloudimg.com/draft/1000053/6gek6a3h29.png?imageView2/2/w/1620)创建脚本 init
其中:`/bin`目录下是常用命令,`init`是自己写的脚本,`/lib64`目录下是应用程序所依赖的动态库。
![img](https://ask.qcloudimg.com/draft/1000053/z282hobegv.png?imageView2/2/w/1620)init 内容
现在我们需要使用命令行,创建`bin`和`sbin`目录,向其中添加`bash`、`ls`、`rm`、`cp`、`mv`、`echo`、`cat`、`less`等基础命令。由于这些命令需要依赖`/lib64`等目录下的一些动态链接的共享库,所以需要将依赖的库拷贝到小系统对应的目录下,用`ldd`命令查询应用程序及其依赖的动态库。完成之后,执行:
```js
find . | cpio -H newc -o | gzip > /boot/initrd.img
```
将根文件系统打包成`initrd.img`放到`/boot`目录下。启动时系统会自动执行`initrd.img`中的`init`。
费了这么大劲生成`initrd.img`,如何测试新建的`initrd.img`呢,需要在`grub`启动配置文件当中增加一个入口用于测试。
```js
title CentOS 6 Mini
root (hd0,0)
kernel /vmlinuz-2.6.32-642.el6.x86_64
initrd /initrd.img
```
这样重启之后就会出现启动选项了。
## Step2:完成**挂载原系统能力**
为了能挂载原系统,必须在`initrd.img`中加载原系统运行所必须的驱动模块,比如`ext4`文件系统的驱动、`scsi`设备的相关驱动等,`/sbin/modinfo` 配合`/sbin/insmod`,驱动放到`/module`
![img](https://ask.qcloudimg.com/draft/1000053/wvl3g0bbka.png?imageView2/2/w/1620)
## Step3:**完成拥有管理设备能力(udev)**
利用管理、监控主机设备的服务程序udevd来自动加载所需的驱动模块,比我们自己实现更加可靠。`udevd`的规则文件在`/lib/udev/`目录下,配置文件在`/etc/udev/`目录下,同时还需要`/etc/nsswitch.conf`配置的名称服务交换,其依赖的库为`/lib`目录下以`libnss`开头的文件,将上述文件拷贝到我们的目录下,然后使用`/sbin/start\_udev`命令可以启动`udevd`服务。(`udevd`需要调用一些其他的系统命令,如`/sbin/modprobe`,可用`strace`进行跟踪获取)。
![img](https://ask.qcloudimg.com/draft/1000053/lksvhwklon.png?imageView2/2/w/1620)小系统的目录文件
其中:`/dev`目录下是系统存放可用设备的目录,`/log`是使用`strace`命令生成的`log`记录文件。
## Step4:**完成拥有login登录能力**
由于`login`的机制比较复杂,涉及进程管理机制和进程组、控制台等许多方面,因此我们采用**\*M2***,将`/sbin/init`命令拷到小系统目录下,`init`脚本改为
```js
#!/bin/bash
exec /sbin/init
```
将控制权交给`/sbin/init`之后,系统启动时就必须等到它完成一系列调用之后,进入`login`界面,用户才能重新获得控制权。
`/sbin/init`的过程大致分为三块:第一块是udevd加载驱动模块、文件系统检查和根切换,相关配置在`/etc/rc.sysinit`中;第二块是启动各项服务,相关配置在`/etc/rc.d/`目录下;第三块是登录部分,需要调用`/sbin/mingetty`和`/bin/login`等命令。将上述所涉及的命令及文件拷贝到小系统对应的目录下,并对配置进行修改。
由于小系统启动之后`initrd.img`作为临时根文件系统直接在内存中运行,而我们小系统不需要进行根切换,故将`/etc/rc.sysinit`中`remount\_needed()`函数体注释掉,这样就不会根切换了。
由于系统采用了全新的`Upstart`启动方式(`/sbin/init`程序已经改由`upstart`软件包提供),将与`Upstart`启动相关的配置文件拷贝至小系统目录下:
```js
/etc/inittab 配置默认运行级别
/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终端的开启
```
将`bootmini/etc/inittab`的运行优先级改为2,那么系统启动时`/sbin/init`将执行`bootmini/etc/rc.d/rc2.d/`目录下以`S`开头的文件,将一些不需要开启的服务文件名改为`K`开头。
在`bootmini/etc/rc.d/rc.local`文件中可以加入用户需要系统开机启动后自动执行的操作。
`login`程序基于认证体系`PAM`, 配置文件在`/etc/pam.d/`目录下,相关库文件有`/lib64/security/`及其依赖的库文件;`login`还涉及用户组管理`/bin/chgrp`、`/bin/chown`、`/bin/chmod`等,保存用户名的文件`/etc/passwd`、`/etc/group`,用户密码文件为`/etc/shadow`。其他一些涉及的文件可通过`strace`来帮助分析。
## **可在真机上运行的完整版小系统**
部分目录文件:
![img](https://ask.qcloudimg.com/draft/1000053/0fds3abkc5.png?imageView2/2/w/1620)/etc
![img](https://ask.qcloudimg.com/draft/1000053/cxmha7kp7a.png?imageView2/2/w/1620)/bin
![img](https://ask.qcloudimg.com/draft/1000053/of5qpwxisv.png?imageView2/2/w/1620)/sbin
![img](https://ask.qcloudimg.com/draft/1000053/6pmmre55m9.png?imageView2/2/w/1620)/usr/bin
![img](https://ask.qcloudimg.com/draft/1000053/ct1qv7g1zn.png?imageView2/2/w/1620)/usr/sbin
至此,文件系统算是可以跑了。下一篇我们再造个轮子——进行`Linux`内核的裁剪。真机效果也将在下篇看到。
**此文已由腾讯云+社区在各渠道发布**
**获取更多新鲜技术干货,可以关注我们[腾讯云技术社区-云加社区官方号及知乎机构号](https://www.zhihu.com/org/teng-xun-yun-ji-zhu-she-qu/activities)**
有疑问加站长微信联系(非本文作者)