来做个mini-Linux吧!

制作一只超 mini 的 Tux !!

tux

在制作之前, 我们一定要熟悉Linux的启动路程啊, 这个部分我已经在不同的文章中重复了三次之多了, 在这里回顾一下Linux内核编译的一些步骤:

首先我们拿到Linux kernel的源代码之后进行解压, 执行make menuconfig 除此之外, 我们还可以直接使用当前系统的.config模板 当然手写也是可以的, 只要确保语法没有什么错误. 接着就可以进行make了, 这里可以使用-j参数加快速度. 编译结束之后, 执行模块安装, 最后才执行整个系统的安装.

但是, 今天不是这样的方式了, 我们要先编译一个非模块的方式, 这样就不需要initrd和ramdisk/ramfs了 这就是内核空间了, 那么文件系统方面呢? 我们移植一个bash其实就可以, 但是这样并不能使用, 他只能用来测试. 所以我们使用busybox. 这是一个很神奇的程序, 他可以模拟你想要实现的基础二进制程序, 这个我们后面再说.

这样就可以得到一个完整的OS了. 但为了能够引导系统, 我们还需要一个bootloader. OK, 现在就开始吧!

我们使用CentOS6来做这个实验, 可以考虑做之前做个快照~

制作开始

首先我们需要安装两个yum包组 – Development tools 和 Server Platform Development. 接着获取源代码.

1
2
yum groupinstall "Server Platform Development" "Development Tools" -y
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.10.67.tar.xz --no-check-certificate

接着我们解压内核:

1
tar -xf linux-3.10.67.tar.xz -C /usr/src

进入目录里, 当时在编译内核的时候我们是使用了一个系统既有的配置模板, 但是现在我们不一样了, 我们要重头制作一个最精简的内核啦, 所以查看一下make的选项:

1
2
3
4
5
6
[root@localhost linux-3.10.67]# make help
...(omitted)
Configuration targets:
...(omitted)
allnoconfig - New config where all options are answered with no
...(omitted)

这个allnoconfig就是我们要使用的选项.

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost linux-3.10.67]# make allnoconfig
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
SHIPPED scripts/kconfig/zconf.tab.c
SHIPPED scripts/kconfig/zconf.lex.c
SHIPPED scripts/kconfig/zconf.hash.c
HOSTCC scripts/kconfig/zconf.tab.o
HOSTLD scripts/kconfig/conf
scripts/kconfig/conf --allnoconfig Kconfig
#
# configuration written to .config
#

这样就得到了我们的.config模板文件. 但是这样只是得到了一份比较干净的模板, 我们接下来要做的就是根据自己的硬件状态按需启用功能. 打开配置菜单:

1
2
3
4
5
6
7
8
9
10
[root@localhost linux-3.10.67]# make menuconfig
HOSTCC scripts/kconfig/lxdialog/checklist.o
HOSTCC scripts/kconfig/lxdialog/inputbox.o
HOSTCC scripts/kconfig/lxdialog/menubox.o
HOSTCC scripts/kconfig/lxdialog/textbox.o
HOSTCC scripts/kconfig/lxdialog/util.o
HOSTCC scripts/kconfig/lxdialog/yesno.o
HOSTCC scripts/kconfig/mconf.o
HOSTLD scripts/kconfig/mconf
scripts/kconfig/mconf Kconfig

在接下来的几个步骤中, 请注意一定要确保开启的选项是和你的实际硬件情况相符合的. 如果你是VMware虚拟机的话, 应该和我的硬件情况差不多, 但还是要注意看啊

其实重点就在于位数, CPU和磁盘驱动, 最重要的就是磁盘驱动了, 接下来的选项如下:

先贴一下我的硬件情况:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@localhost ~]# lspci
00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)
00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)
00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:07.7 System peripheral: VMware Virtual Machine Communication Interface (rev 10)
00:0f.0 VGA compatible controller: VMware SVGA II Adapter
00:10.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)
00:11.0 PCI bridge: VMware PCI bridge (rev 02)
00:15.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:15.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:16.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:17.7 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.1 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.2 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.3 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.4 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.5 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.6 PCI bridge: VMware PCI Express Root Port (rev 01)
00:18.7 PCI bridge: VMware PCI Express Root Port (rev 01)
02:00.0 USB controller: VMware USB1.1 UHCI Controller
02:01.0 Ethernet controller: Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE] (rev 10)
02:02.0 Multimedia audio controller: Ensoniq ES1371 [AudioPCI-97] (rev 02)
02:03.0 USB controller: VMware USB2 EHCI Controller
02:05.0 Ethernet controller: Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE] (rev 10)

64bit

打开64位支持. 接着进入处理器配置: ( Processor type and features )

multi-processing

接着进入PCI总线的配置:
pci

打开支持. 其他都不用做.

接着进入Device Drivers > SCSI device support ( 这里是因为我的设备是SCSI啊 )

scsi0

打开SCSI的底层协议支持, 接着回到上级, 由于我是MPT协议所以打开支持:
mpt

进去之后就会看到SPI的驱动支持和MPT驱动以及日志, 编译进内核:
mpt2

接着保存退出就可以了.

接着就到了编译的时候了, 由于没有模块的参与, 所以直接编译一个内核就可以了:

1
[root@localhost linux-3.10.67]# make -j 2 bzImage

其实我们可以新建一个硬盘加入进来, 安装引导, 将内核放进去.

如果这个时候你已经编译完了, 可以看一下报告:

1
2
3
4
Setup is 13628 bytes (padded to 13824 bytes).
System is 1448 kB
CRC 1c46d854
Kernel: arch/x86/boot/bzImage is ready (#1)

内核的大小是1448 kB 怎么样? 够小了吧.

我添加了一块新的SCSI硬盘, 大小为10G, 其实2G都足够了.

OK, 开始分区划分文件系统:

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
27
28
29
30
31
32
33
34
35
36
[root@localhost ~]# fdisk /dev/sdb
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0xc4f29f1a.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
switch off the mode (command 'c') and change display units to
sectors (command 'u').

Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-1305, default 1): 1
Last cylinder, +cylinders or +size{K,M,G} (1-1305, default 1305): +100M

Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 2
First cylinder (15-1305, default 15):
Using default value 15
Last cylinder, +cylinders or +size{K,M,G} (15-1305, default 1305): +2G

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

100M用于内核, 2G用于根文件系统.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
[root@localhost ~]# mkfs.ext4 /dev/sdb1
mke2fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
28112 inodes, 112420 blocks
5621 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=67371008
14 block groups
8192 blocks per group, 8192 fragments per group
2008 inodes per group
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729

Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 35 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
[root@localhost ~]# mkfs.ext4 /dev/sdb2
mke2fs 1.41.12 (17-May-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
131648 inodes, 526128 blocks
26306 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=541065216
17 block groups
32768 blocks per group, 32768 fragments per group
7744 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912

Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 35 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.

文件系统也OK了, 这样就可以开始挂载了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost ~]# mkdir /mnt/{boot,sysroot}
[root@localhost ~]# mount /dev/sdb1 /mnt/boot/
[root@localhost ~]# grub-install --root-directory=/mnt /dev/sdb
Probing devices to guess BIOS drives. This may take a long time.
Installation finished. No error reported.
This is the contents of the device map /mnt/boot/grub/device.map.
Check if this is correct or not. If any of the lines is incorrect,
fix it and re-run the script `grub-install'.

(fd0) /dev/fd0
(hd0) /dev/sda
(hd1) /dev/sdb
[root@localhost ~]# ls /mnt/boot/
grub lost+found
[root@localhost ~]# mount /dev/sdb2 /mnt/sysroot/

建立引导完成. 我们现在把刚刚编译结束的内核拷贝过来:

1
2
3
4
[root@localhost ~]# cp -av /usr/src/linux-3.10.67/arch/x86/boot/bzImage /mnt/boot/bzImage
`/usr/src/linux-3.10.67/arch/x86/boot/bzImage' -> `/mnt/boot/bzImage'
[root@localhost ~]# file /mnt/boot/bzImage
/mnt/boot/bzImage: Linux kernel x86 boot executable bzImage, version 3.10.67 (root@localhost.localdo, RO-rootFS, swap_dev 0x1, Normal VGA

我们的这个mini内核能够驱动SCSI硬盘,所以我们不需要ramdisk了, 接下来编辑grub.conf配置使得引导内核就行了.

1
2
3
4
5
default=0
timeout=3
title Mini Linux(3.10.67)
root (hd0,0)
kernel /bzImage ro root=/dev/sda2

上面就是我们的grub.conf. 好了, 使用sync写入到磁盘. 挂起虚拟机!

接着, 新建虚拟机 –> 不选择系统, 选择之前创建的那个虚拟SCSI硬盘, 配置完成.

激动人心的时刻到来~ 开机~

接着, 内核炸啦 ! ! !

panic

看一下panic信息: 没有文件系统可以挂载根. 原因很简单, 我们没法驱动文件系统….所以只能重新编译内核, 加入文件系统.

fs

进入文件系统设置, 直接把ext家族都选进内核. 除此之外, 我们还要进入上级目录中的可执行文件格式, 打开ELF支持和#!支持.

elf

退出并保存.

好了, 编译吧.

编译结束之后, 我们把新的覆盖掉. 还是要先同步数据接着挂起.

重新启动小企鹅 ! ! !

……

啊 ! 内核又被我们吓死了!

noinit

但是查看一些信息, 我们发现, 文件系统已经挂载上去了. 内核被吓死的原因其实是因为没有init程序.

所以接下来, 就到了busybox登场的时候了!

移植bash shell

首先我们先来了解一下主程序是什么玩意, 在源码的init目录下, 有个main.c文件:

1
[root@localhost ~]# vim /usr/src/linux-3.10.67/init/main.c

840行左右有个:

exec

这就是尝试加载的顺序. 可以看出来, 即使你只有个sh程序, 也是可以跑的.

在使用busybox模拟之前, 我们先尝试移植一个bash shell过去好了

先来手动模拟个根, 进入sysroot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@localhost sysroot]# mkdir -pv bin dev etc home lib lib64 media mnt proc root sbin sys tmp usr/{bin,sbin,lib,lib64} var/{log,run,lock}
mkdir: created directory `bin'
mkdir: created directory `dev'
mkdir: created directory `etc'
mkdir: created directory `home'
mkdir: created directory `lib'
mkdir: created directory `lib64'
mkdir: created directory `media'
mkdir: created directory `mnt'
mkdir: created directory `proc'
mkdir: created directory `root'
mkdir: created directory `sbin'
mkdir: created directory `sys'
mkdir: created directory `tmp'
mkdir: created directory `usr'
mkdir: created directory `usr/bin'
mkdir: created directory `usr/sbin'
mkdir: created directory `usr/lib'
mkdir: created directory `usr/lib64'
mkdir: created directory `var'
mkdir: created directory `var/log'
mkdir: created directory `var/run'
mkdir: created directory `var/lock'

为了方便移植, 我们写一个脚本, 贴在下面:

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
27
#!/bin/bash
#
target=/mnt/sysroot
[ -d $target ] || mkdir $target

read -p 'A command: ' command

libcp() {
for lib in $(ldd $1 | grep -o "[^[:space:]]*/lib[^[:space:]]*"); do
libdir=$(dirname $lib)
[ -d $target$libdir ] || mkdir -p $target$libdir
[ -f $target$lib ] || cp $lib $target$lib
done
}

while [ "$command" != 'quit' ]; do
if ! which $command &> /dev/null; then
read -p "No such command.enter again: " command
continue
fi
command=$(which --skip-alias $command)
cmddir=$(dirname $command)
[ -d $cmddir ] || mkdir -p $target$cmddir
[ -d $target$command ] || cp $command $target$command
libcp $command
read -p "Another command? (enter quit to exit) " command
done

使用这个脚本我们把bash和ls程序复制到了新的根里.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@localhost ~]# tree /mnt/sysroot/
/mnt/sysroot/
├── bin
│ ├── bash
│ └── ls
├── dev
├── etc
├── home
├── lib
├── lib64
│ ├── ld-linux-x86-64.so.2
│ ├── libacl.so.1
│ ├── libattr.so.1
│ ├── libcap.so.2
│ ├── libc.so.6
│ ├── libdl.so.2
│ ├── libpthread.so.0
│ ├── librt.so.1
│ ├── libselinux.so.1
│ └── libtinfo.so.5
....(omitted)

好了, 我们在 grub.conf 中加入 init=/bin/bash 明确告诉内核如何启动.

还是一样的步骤: 写入磁盘, 挂起, 重启小企鹅.

bash!

终于! 内核没有恐慌啦. 但是你又发现, 怎么敲, 小企鹅也没反应.

原因很简单, 没有键盘驱动. 那么再来一次吧…这次要把输入设备的驱动支持加入到内核中.

keyboard

加入鼠标和键盘支持, 注意这里的鼠标是PS2的, 而VMware模拟的鼠标是USB驱动的, 所以还要开启USB驱动支持:

usb

这里我把USB1.1, 2.0, 3.0的驱动都打开了.

编译内核,拷贝,重复磁盘写入,挂起步骤.

bashNew

小企鹅站起来啦!

那么接下来, 我们使用脚本来模拟一下init程序, 在/mnt/sysroot/sbin/init中写上:

1
2
3
4
5
6
7
#!/bin/bash
#
echo -e "\tWelcome to \033[32mMini Linux\033[0m"
mount -n -t proc proc /proc
mount -n -t sysfs sysfs /sys
mount -n -o remount,rw /dev/sda2 /
/bin/bash

再给他加几个程序:

1
2
3
4
5
6
7
[root@localhost sysroot]# bash ~/cp.sh 
A command:touch
Another command? (enter quit to exit) mount
Another command? (enter quit to exit) blkid
Another command? (enter quit to exit) top
Another command? (enter quit to exit) ps
Another command? (enter quit to exit) quit

再来!

进去之后发现还是没有/proc和/sys…没关系, 手动再挂载一次. 这样就可以看到当前的挂载情况了:
mountOK

但是你会发现, 我们不能把磁盘挂载过来, 原因是/dev下是空的. 这个我们之前也说过dev下的设备文件是由udev创建的 那怎么办? 没关系, 我们的内核提供了这个功能, 可以不依靠udev, 我们 回到宿主机重新打开menuconfig:

maindevNew

再来吧.

new

这样我们的dev下就可以看到设备文件了. 这就是那个选项的功用.

接着我们把sda2挂载到根目录下就好了:

1
bash-4.1# mount -n -t ext4 -o remount,rw /dev/sda2 /

完成! 但是现在离我们的目标还差一个重要的部分, 还记得原本我们计划使用busybox来模拟用户空间的吗. 所以现在就要编译一个busybox给我们的小Tux使用啦

busybox

这里使用的busybox版本是1.22.1(Stable).

首先获取 源代码 , 将其解压之后可以通过INSTALL文档来获得帮助.

1
2
3
[root@localhost ~]# tar xf busybox-1.22.1.tar.bz2 
[root@localhost ~]# cd busybox-1.22.1
[root@localhost busybox-1.22.1]# less INSTALL

你会发现,busybox的构建方式和内核特别像.

在编译之前, 显然我们的busybox应该编译成为一个单独的应用程序. 这就需要进行静态编译 也就是不使用共享的链接库, 我们的glibc提供了一个专门用于进行静态编译的包:

1
2
3
4
5
6
7
8
9
10
11
Installed Packages
glibc.x86_64
glibc-common.x86_64
glibc-devel.x86_64
glibc-headers.x86_64
Available Packages
glibc.i686
glibc-devel.i686
glibc-static.i686
glibc-static.x86_64
glibc-utils.x86_64

就是那个static了, 如果没有安装devel的话还是要先安装这个的( 不过一般机器都是装过的吧

接着就可以开始进行配置和编译了.

build0

进去之后你会发现, 这个界面和我们编译内核的配置界面很相似, 这个就是tui嘛. 我们要注意的选项是Build Options,

进去之后看到的第一项就是我们要勾选的:

static0

接着回到上级, 我们进入make Install行为设置:

makeInstall

我们可以直接把安装的位置放到/mnt/sysroot目录下, 也可以不修改直接编译, 反正复制过去就可以了.

那么接下来保存退出就好, 执行make && make install

既然使用了busybox, 我们就不需要原先手动创建的那些目录了, 将他们删除:

1
2
3
4
5
6
[root@localhost ~]# cd /mnt/sysroot/
[root@localhost sysroot]# ls
bin dev etc home lib lib64 lost+found media mnt proc root sbin sys tmp usr var
[root@localhost sysroot]# rm -rf ./*
[root@localhost sysroot]# ls
[root@localhost sysroot]#

编译完成了之后, 我们把所有的_install下的文件复制到/mnt/sysroot上去.

1
[root@localhost busybox-1.22.1]# cp -a ./_install/* /mnt/sysroot/

来确认一下:

1
2
3
4
5
6
[root@localhost sysroot]# ll
total 12
drwxr-xr-x. 2 root root 4096 Oct 2 11:51 bin
lrwxrwxrwx. 1 root root 11 Oct 2 11:51 linuxrc -> bin/busybox
drwxr-xr-x. 2 root root 4096 Oct 2 11:51 sbin
drwxr-xr-x. 4 root root 4096 Oct 2 11:51 usr

其中有个软链, 这个是在模拟init程序, 我们并不需要这个, 所以也把他删除吧. 因为我们有个init程序在sbin目录下:

1
2
[root@localhost sysroot]# ls sbin/ | grep init
init

这样就可以改写grub.conf文件, 将启动参数指向这个程序就行了. 不过为了保险起见, 我们还是把一些目录创建一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@localhost sysroot]# mkdir -pv tmp proc sys etc home root mnt media var lib lib64 dev boot
mkdir: created directory `tmp'
mkdir: created directory `proc'
mkdir: created directory `sys'
mkdir: created directory `etc'
mkdir: created directory `home'
mkdir: created directory `root'
mkdir: created directory `mnt'
mkdir: created directory `media'
mkdir: created directory `var'
mkdir: created directory `lib'
mkdir: created directory `lib64'
mkdir: created directory `dev'
mkdir: created directory `boot'

没完, 这个sbin/init程序是需要读取etc/inittab来工作的, 所以我们需要提供这样一个文件:

1
2
3
4
::sysinit:/etc/rc.d/rc.sysinit
console::respawn:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/mount -a -r

但是现在也没有这个rc.sysinit脚本啊, 只好手写一个了:

1
2
3
4
5
6
#!/bin/sh
#
echo -e "\tWelcome to \033[32mMini Linux\033[0m !"
mount -n -t proc proc /proc
mount -n -t sysfs sysfs /sys
mount -o remount,rw /dev/sda2 /

由于inittab已经开启了一个console了, 所以不需要再启动shell了. 赋予执行权限之后再来一次就好了.

welcom

看到那个提示语了么! 这就说明init成功了!

踩坑提示: sh不支持-n参数, 所以这个地方应该把脚本中的-n参数弄掉

我们来完善这个小企鹅, 加上fstab:

1
2
3
4
5
6
[root@localhost ~]# vim /mnt/sysroot/etc/fstab

proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
/dev/sda1 /boot ext4 defaults 0 0
/dev/sda2 / ext4 defaults 0 0

并且完善我们的启动脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/sh
#
echo -e "\tWelcome to \033[32mMini Linux\033[0m !"
mount -t proc proc /proc
mount -t sysfs sysfs /sys

echo "scan /sys and populate /dev..."
mdev -s

mount -o remount,rw /dev/sda2 /

echo "mounting all file systems..."
mount -a

其中有个mdev, 这个就是充当udev功能的.

接着, 完善我们的启动终端:

1
2
3
4
5
6
::sysinit:/etc/rc.d/rc.sysinit
tty1::askfirst:/bin/sh
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/mount -a -r

重启进入系统, 这个时候你如果使用Alt+f2来切换的话, 就会看到:
active

这就是askfirst的效果. 但是mount还是有问题. 考虑到busybox的Bug, 我们把系统上的mount复制过去.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost sysroot]# cd bin/
[root@localhost bin]# ls
ash chgrp cttyhack dumpkmap fgrep hostname kill lsattr more netstat printenv rmdir setserial sync usleep
base64 chmod date echo fsync hush linux32 lzop mount nice ps rpm sh tar vi
busybox chown dd ed getopt ionice linux64 makemime mountpoint pidof pwd run-parts sleep touch watch
cat conspy df egrep grep iostat ln mkdir mpstat ping reformime scriptreplay stat true zcat
catv cp dmesg false gunzip ipcalc login mknod mt ping6 rev sed stty umount
chattr cpio dnsdomainname fdflush gzip kbd_mode ls mktemp mv pipe_progress rm setarch su uname
[root@localhost bin]# mv mount mount.orig
[root@localhost bin]# bash ~/cp.sh
A command:mount
Another command? (enter quit to exit) bash
Another command? (enter quit to exit) quit
[root@localhost bin]# ls
ash chattr cpio dnsdomainname fdflush gzip kbd_mode ls mktemp mt ping6 rev sed stty umount
base64 chgrp cttyhack dumpkmap fgrep hostname kill lsattr more mv pipe_progress rm setarch su uname
bash chmod date echo fsync hush linux32 lzop mount netstat printenv rmdir setserial sync usleep
busybox chown dd ed getopt ionice linux64 makemime mount.orig nice ps rpm sh tar vi
cat conspy df egrep grep iostat ln mkdir mountpoint pidof pwd run-parts sleep touch watch
catv cp dmesg false gunzip ipcalc login mknod

行了, 再试一次吧.

suc1

这一次成功挂载! 一个较完整的小企鹅站起来啦! 但是作为Linux内核, 没有网络功能岂不是很讽刺 所以我们要给他加上网络功能

( 其实这样折腾还蛮累的…. )

首先打开网络支持:
networkingsupport

接着进入选项打开TCP/IP网络支持, 如果你有兴致也可以把多播和高级路由打开.

tcpip

接着就是网卡驱动的支持:
networkdriver

具体到Ethernet网卡的驱动:
ethernet0

这里面有很多无用的驱动, 都把他们关掉. 只要打开你自己网卡的支持就行了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@localhost ~]# lspci
00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)
00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)
00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:07.7 System peripheral: VMware Virtual Machine Communication Interface (rev 10)
00:0f.0 VGA compatible controller: VMware SVGA II Adapter
00:10.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)
00:11.0 PCI bridge: VMware PCI bridge (rev 02)
...(omitted)
02:00.0 USB controller: VMware USB1.1 UHCI Controller
02:01.0 Ethernet controller: Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE] (rev 10)
...(omitted)

amd1

这样就行了, 保存编译吧

1
2
3
[root@localhost linux-3.10.67]# cp -av arch/x86/boot/bzImage /mnt/boot/
cp: overwrite `/mnt/boot/bzImage'? y
`arch/x86/boot/bzImage' -> `/mnt/boot/bzImage'

我被坑了. 我就纳闷明明物理机的网卡是Intel e1000怎么模拟了个AMD, 其实我是Intel的NIC啊,,,所以后来又重新编译了一次, 就不上图了…心累

接着就会看到:

ifconfig0

配置上IP启用就可以正常的Ping了.

再来折腾点新的玩意, 这一次我们把网络驱动编译成模块.

ntel

接着正常编译, 但这次不同的是, 我们需要额外的编译一个单独的模块.

这样来:

1
2
3
4
5
6
7
8
[root@localhost linux-3.10.67]# make M=drivers/net/ethernet/intel/e1000/
Building modules, stage 2.
MODPOST 1 modules
LD [M] drivers/net/ethernet/intel/e1000/e1000.ko
[root@localhost linux-3.10.67]# mkdir /mnt/sysroot/lib/modules -pv
mkdir: created directory `/mnt/sysroot/lib/modules'
[root@localhost linux-3.10.67]# cp drivers/net/ethernet/intel/e1000/e1000.ko /mnt/sysroot/lib/modules/
[root@localhost linux-3.10.67]#

重新开机:

ins0

果然, 没有了驱动, mini Tux无法认识他的新朋友Intel e1000网卡. 但是我们可以插一下模块, Tux就会认识了:

ins1

现在就可以再次验证:

ins2

大功告成. 这样我们就继续补充配置我们小企鹅了.

在rc.sysinit中加入:

1
2
3
4
5
6
7
8
9
10
echo "loading Intel e1000..."
insmod lib/modules/e1000.ko

echo "Initializing NIC..."
ifconfig eth1 192.168.206.133 up
ifconfig lo 127.0.0.1 up

[ -f /etc/sysconfig/network ] && . /etc/sysconfig/network
[ -z "$HOSTNAME" -o $HOSTNAME == '(none)' ] && HOSTNAME='localhost'
hostname $HOSTNAME

接着创建/etc/sysconfig/network, 向里面echo一句:

1
2
[root@localhost sysroot]# mkdir etc/sysconfig
[root@localhost sysroot]# echo "HOSTNAME=miniTux.yaoxuannn.com" > etc/sysconfig/network

除了网络的初始化, 我们还想让这只小企鹅增加用户用户认证的功能, 怎么办?

步骤如下:

  • 创建用户, 以及所需要的文件(passwd, group, shadow)
  • 修改inittab, 运行getty.
  • 最好使用简单的加密算法

动手!

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# chroot /mnt/sysroot/ /bin/sh
/ # adduser root
adduser: /etc/passwd: No such file or directory
/ # touch /etc/passwd /etc/shadow /etc/group
/ # adduser root
passwd: unknown uid 0
/ # vim /etc/passwd
/bin/sh: vim: not found
/ # vi /etc/passwd
/ # vi /etc/group
/ # # file shadow is a little difficult to handle...I choose to copy one line from the real root...
/ #

( 真的心累…..

1
2
3
4
5
6
[root@localhost ~]# head -1 /etc/shadow > /mnt/sysroot/etc/shadow
[root@localhost ~]# openssl passwd -1 -salt $(openssl rand -hex 4)
Password:
$1$e1f8a47b$sdg4zhqqBfNE..EMno.q60
[root@localhost ~]# vim /mnt/sysroot/etc/shadow
[root@localhost ~]# chmod 400 /mnt/sysroot/etc/shadow

接下来就可以去搞我们的初始化脚本了:

1
2
3
4
5
6
::sysinit:/etc/rc.d/rc.sysinit
::respawn:/sbin/getty 9600 tty1
::respawn:/sbin/getty 9600 tty2
::respawn:/sbin/getty 9600 tty3
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/mount -a -r

挂起, 打开小企鹅!

It works! 我们的脚本工作了, 但是一敲回车就进入sh了..所以还是不行, 看样子是哪里有问题. ( 难道是busybox的Bug?

折腾不动了, 后面有空再填坑, 拜拜