Mini linux 制作过程
Linux启动流程:
Centos6:POST(加电自检)→BootSequence(BIOS)→ BootLoader(引导加载器)→ Kernel(ramdisk)→/sbin/init
Centos7:POST(加电自检)→BootSequence(BIOS)→ BootLoader(引导加载器)→ Kernel(ramdisk)→/sbin/systemd
POST:开机检查硬件信息;
BootSequence:根据BIOS中设置的启动顺序,自上而下寻找拥有启动程序的设备(以硬盘为例,就是去寻找具有MBR的设备,并且其中装载了具有BootLoader的硬盘设备);
BootLoader: 用于装载指定设备上的内核和ramdisk;
lilo:最古老的引导器,下载广泛用于安卓手机中;
grub legacy:用于centos6中;
grub2:用于centos7中;
grub将引导加载的过程分成了两个阶段(也可以看成是2.5个阶段):
stage1:在MBR中实现;
stage1.5:提供对分区上的文件系统的支持;
stage2:grub主程序;
Kernel:加载内核,内核会进行初始化,重新探测各种硬件信息,然后以只读方式挂载文件系统,接着执行根文件系统中的第一个应用程序(init);但是kernel未必能直接识别根文件系统所在的磁盘设备,如果不能识别就需要借助ramdisk,才可以加载根文件系统,如果能识别那么ramdisk就是可有可无的;
ramdisk:(有点类似于win pe)是一个小的linux,提供一个虚根,借助它来装载磁盘设备的驱动程序,进而去加载真正的根文件系统,然后从虚根切换到真正的根;它是只读的;
/sbin/init:可以看成是一个脚本文件,借助bash解释器完成向内核提交个进程启动请求;
对于Centos5来说是读取/etc/inittab这个文件;
对于Centos6来说是读取/etc/init/*.conf这些文件;
它俩实现的功能是类似的:
1.设置默认运行级别;
2.运行系统初始化脚本:/etc/rc.d/rc.sysinit;
/etc/rc.d/rc $runlevel
3.启动终端,并运行login
4.如果是level5的话,就会启动图形终端
/sbin/systemd:不需要借助任何bash脚本,是一套完整的自成体系的服务启动程序;
Centos6通过编写服务脚本和编辑upstart配置文件来实现服务的管理;
Centos7也可以通过编写服务脚本来管理服务,但是它已经不再使用upstart了,而是使用全新的systemd来管理服务,因为systemd使用更方便,功能更强大;
内核编译:
make menuconfig → 生成.config文件
make [-j #] → 指明编译时启动的线程数
make modules_install → 安装模块
make install
mini linux制作步骤:
0.bootloader:grub
1.手动编译一个内核(以非模块化编译,也就是将所有需要的功能都编译进内核,所以ramdisk就用不到了);
2.提供一个根文件系统:busybox,它可以模拟用户空间当中所需要的各种常见应用程序;
操作步骤:(下面的操作会有反复的重启和各种错误以及修补这些错误,所以可能会造成极度的不适,请做好心里准备;)
1.首先安装开发工具;
yum groupinstall "Development Tools" "Server Platform Development" -y
2.在内核官网下载内核源码,将其解压到/usr/src/中,并且创建软连接linux连接到刚才解压的目录上;
tar xf linux-3.18.98.tar.xz -C /usr/src/
ln -sv linux-3.18.98/ linux
make help 获取帮助信息
3.因为我们要编译一个非常精简的内核所以要将内核中默认提供的所有可选择的选项都关掉;
make allnoconfig
我使用的内核版本在执行完这个操作进入编译界面以后,发现在选择关于scsi的信息的时候,里面没有选项,我也没有找到可以将它的选项显示出来的关联选项;所以我使用的是默认的配置,然后一条一条的将没有用的选项手动去掉的,一般为模块(M)的都可以去掉;你可能会发现我下面写的步骤中,有的你已经支持了并且可以运行,但是我希望你能做完,这会对你有很大的帮助;
4.编译内核前需要检查硬件信息:比如硬盘接口信息,CPU型号等信息
lscpu 查看cpu类型
lspci 查看各种接口类型
SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X
Fusion-MPT Dual Ultra320 SCSI (rev 01)
5.根据输出的信息编译内核;
make menuconfig
[*] 64-bit kernel 因为本机是64位的,一会还要将本机的一些命令复制到mini linux中所以我们也要将内核编译为64位,为了兼容;
[*] Enable loadable module support ---> 支持加载模块
[*] Module unloading 支持动态卸载模块
[ ] Module signature verification 这一项是实现对模块进行签名验证,是为了提高安全性,可以想像我们装载一个来路不明的模块,如果这个模块是被人动了手脚的,那么导致的结果就是连最基础的内核都被人黑了,还有什么安全可言,可能唯有重装系统才可以解决了吧,但是我们完全是自己做个小linux玩,所以就不启动了;
Processor type and features --->
[*] Symmetric multi-processing support 配置cpu支持多核心;
[*] Multi-core scheduler support
[*] Sparse Memory virtual memmap
Processor family (Generic-x86-64) --→ 选择与平台相兼容的cpu接口类型;根据实际情况选择即可;
(X) Generic-x86-64 实在不知道就选择这个通用的;
Bus options (PCI etc.) ---> pci总线功能;
[*] PCI support 启动pci总线功能;
Device Drivers ---> 硬件驱动;
SCSI device support ---> SCSI设备支持选项;SCSI即是一种存储协议,也是一种存储接口类型,还是一种存储总线,而这种存储协议只能工作在这种存储接口和这种存储总线上;
<*> SCSI disk support 支持SCSI硬盘
[*] Fusion MPT device support --→ 支持相应的硬盘接口驱动程序;
<*> Fusion MPT ScsiHost drivers for SPI SCSI接口
<*> Fusion MPT ScsiHost drivers for SAS SAS接口
<*> Fusion MPT misc device (ioctl) drive MPT设备驱动
[*] Fusion MPT logging facility 日志
选择我们要编译进内核的功能:包括对底层硬件的支持,识别各种硬盘接口什么的;
6.添加一个存放这个内核文件的硬盘,并且安装grub;
关闭虚拟机,添加一块磁盘,大小5G,类型为scsi,然后存放到一个你可以找到的位置即可;(我使用的是vmware)
7.开机,将刚才添加的磁盘分区;一般在linux中至少要有三个分区(/,swap,boot(其实boot可以不分区,与根在一起也行));
gdisk /dev/sdc
Number Start (sector) End (sector) Size Code Name
1 2048 206847 100.0 MiB 8300 Linux filesystem
2 206848 4401151 2.0 GiB 8300 Linux filesystem
mkfs.ext4 /dev/sdc1
mkfs.ext4 /dev/sdc2
mkfs.ext4 /dev/sdc1
mount /dev/sdc1 /mnt/boot/
mount /dev/sdc2 /mnt/sysroot/
grub-install --root-directory=/mnt /dev/sdc
cp /usr/src/linux/arch/x86/boot/bzImage /mnt/boot/
chmod +x /mnt/boot/bzImage
file boot/bzImage
boot/bzImage: Linux kernel x86 boot executable bzImage, version 3.18.98 (root@Centos6) #1 SMP S, RO-rootFS, swap_dev 0x3, Normal VGA
vim boot/grub/grub.conf
default=0
timeout=3
title Mini Linux(3.18.98)
root (hd0,0)
kernel /bzImage ro root=/dev/sda2
8.将此虚拟机挂起,然后添加一个新的虚拟机,类型为centos6,内存大小为256M,使用现有的虚拟机(就是我们直线创建的那个磁盘的存储位置);
9.然后开机,然后恭喜你成功捕获一个kernel panic;
内核恐慌的原因是:无法挂载根文件系统,这是因为我们在编译内核的时候并没有 进行支持某种文件系统的编译;所以你懂的!
10.关闭新添加的虚拟机,开启原虚拟机;添加我们会使用到的文件系统,然后再次编译内核;
make menuconfig
File systems ---> 关于文件系统的选项;
<*> Second extended fs support 支持ext2文件系统
<*> Ext3 journalling file system support 支持ext3文件系统
[*] Default to 'data=ordered' in ext3
[*] Ext3 extended attributes
<*> The Extended 4 (ext4) filesystem 支持ext4文件系统
[*] Ext4 POSIX Access Control Lists 持文件的访问控制列表
[*] Ext4 Security Labels
<*> XFS filesystem support 支持xfs文件系统
[*] XFS Quota support
[*] XFS POSIX ACL support
Executable file formats / Emulations ---> 二进制应用程序的各种格式选项;
[*] Kernel support for ELF binaries 支持ELF格式的二进制可执行程序;
[*] Write ELF core dumps with partial segments
<*> Kernel support for scripts starting with #! 支持以#!开头的shell脚本文件;
make -j 2 bzImage
cp /usr/src/linux/arch/x86/boot/bzImage /mnt/boot/
chmod +x /mnt/boot/bzImage
sync
sync
11.挂起此虚拟机,开启新添加的那个虚拟机;然后还要恭喜你,又获得一个kernel painc;
这一次内核恐慌的原因是:没有发现init程序;因为我们的根中什么都没有嘛;
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
这段代码是内核中的一段源码(内核源码目录中init/main.c),它的作用是指明系统启动后如何加载init程序,执行过程是当内核挂载根文件系统后,首先会寻找/sbin/init,如果没有就依次向下寻找;如果都没有就报错;或者使用init手动指定一个;比如init=/bin/bash;
12.常规关闭这个新建的虚拟机,开启原虚拟机;给minilinux创建根中的目录;
mkdir -pv etc dev home proc sys usr lib lib64 usr/lib usr/local usr/lib64 usr/bin usr/sbin bin sbin lib/modules var/{log,run,lock} tmp mnt media root
13.复制当前系统的bash程序;
linux中的程序复制,可不是直接将二进制文件复制过去就可以运行,每个程序运行都是要依赖各种各样的库的,所以我们要将库一并复制过去,使用ldd命令就可以查看程序所依赖的库,但是程序依赖的库实在是太多了,所以我们来写个脚本吧;
vim lddcp.sh
#!/bin/bash
#
#
#
target=/mnt/sysroot
[ -d $target ] || mkdir /mnt/sysroot
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)
cmnddir=$(dirname $command)
[ -d $target$cmnddir ] || mkdir -p $target$cmnddir
[ -f $target$command ] || cp $command $target$command
libcp $command
read -p "Another command(quit):" command
done
chmod +x lddcp.sh
运行脚本文件,将bash,ls,touch,ps,top,mount,df,du,lsblk,blkid等命令添加进去;有的命令你可能发现运行不了,可以通过绝对路径来执行;
14.编辑/mnt/boot/grub/grub.conf
在kernel所在行的末尾添加init=/bin/bash
sync
sync
15.挂起虚拟机,开启新添加的虚拟机,然后你还是一脸懵逼,因为你还是无法输入啊,按键盘根本没有反应,是不是没成功,实则不然,没有反应是因为我们编译内核的时候没有将输入输出设备的驱动编译进内核,所以你懂的!;
16.添加我们会用到的输入输出设备的驱动,然后重新编译内核;
lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
make menuconfig
Device Drivers --->
Input device support ---> 输入设备支持
[*] Keyboards ---> 键盘
<*> AT keyboard AT键盘
[*] USB support ---> 支持usb,因为vmware虚拟的就是usb类型;
<*> Support for Host-side USB
<*> EHCI HCD (USB 2.0) support
<*> xHCI HCD (USB 3.0) support
<*> OHCI HCD (USB 1.1) support
<*> OHCI support for PCI-bus USB controllers
<*> UHCI HCD (most Intel and VIA) support
<*> Mouse interface 启动鼠标接口
[*] Mice ---> 鼠标选项
make -j 2 bzImage
cp /usr/src/linux/arch/x86/boot/bzImage /mnt/boot/
chmod +x /mnt/boot/bzImage
17.挂起虚拟机,开启新添加的虚拟机;
接下来就可以使用shell了!
18.基本上差不多了,这时候我们大可歇一会了!
玩一玩mini linux的shell,其实也没什么好玩的,假装一下吧!
19.好了,差不多休息了三秒钟,我们继续!
20.添加一个 开机后立即执行的脚本(/sbin/init)
21.关闭新添加的虚拟机,开启原虚拟机,编写init;
vim /mnt/sysroot/sbin/init
#!/bin/bash
#
#
#
echo -e " Welcome to