制作一个mini linux,需要对linux的启动流程很熟悉,这里又一次学习Linux的启动过程。
启动流程:
CentOS 6 / 5:
POST -->BootSequence(BIOS) --> BootLoader --> kernel (ramdisk) --> rootfs --> /sbin/init
然后就涉及编写服务脚本(centos5/6),或这编写upstart配置文件(centos6)
对于ramdisk,是用于提供设备驱动的,主要是根文件系统的设备驱动,如果根文件系统所在设备的驱动编译进内核了,那么就不需要加载ramdisk了,凡是编译进内核的驱动,都可以直接使用其设备。而一般的,设备驱动不会直接编译进内核,主要是控制内核的大小,否则内核会很大。设备驱动编译成模块,按需加载。
对于/sbin/init,CentOS5是读取/etc/inittab配置文件,执行其中的配置,而CentOS6则是读取/etc/init/*.conf配置文件,执行其中的配置。这些配置文件要完成的功能:
- 确定默认执行级别
- 运行系统初始化脚本:/etc/rc.d/rc.sysinit
- /etc/rc.d/rc $runlevel
- 启动终端,并运行login
- 启动图形终端(运行级别5)
CentOS 7:
POST -->BootSequence(BIOS) --> BootLoader -->kernel (ramdisk) -->rootfs --> /sbin/systemd
编写服务脚本
systemd unit文件
总体来说,一个最基本的linux系统,就是引导设备上的MBR中的bootloader装载linux内核,然后切换根文件系统,最后启动init(或systemd)。
bootloader: lilo、grub legacy、grub2
grub:stage1:mbr
stage1_5:filesystem driver
stage2:grub的各项加载选项等。
内核编译:
make menuconfig --> .config (主要生成.config文件)
make [ -j # ]
make modules_install (安装模块)
make install
Mini Linux制作步骤:
bootloader:grub安装
内核: kernel(非模块方式,所有驱动编译进内核)
根文件系统:busybox (模拟实现用户空间中的各种程序)
1、环境配置,编译内核需要安装“Development Tools”和“Server Platform Development”软件包组
2、下载内核源代码:www.kernel.org,测试下载了linux-4.19.321.tar.xz。
3、解压缩,tar xf linux-4.19.321.tar.xz -C /usr/src
创建解压后目录的链接文件: ln -sv linux-4.19.321 linux
4、内核的配置,一般在安装完成的机器上都有,如:/boot/config-2.6.32-754.el6.x86_64,因为这里是测试做一个最精简的linux,一切由自己手动配置。
首先把内核可选择的特性全部关闭掉: make allnoconfig
执行完毕后,会在当前目录下生成.config文件
要编译内核,需要提前了解硬件特性,如cpu、pci,尤其要注意硬盘接口
然后执行make menuconfig,对内核特性进行配置
64-bit kernel要选择,要编译成64位的内核系统;
Enable loadable module support 要选择,支持模块动态加载特性,进入子菜单:
Module unloading选上,支持模块动态卸载。
Processor type and features --->选择处理器型号、特性,即支持的CPU型号,进入子菜单
进入Processor family(Generic-x86-64)--->
Generic-x86-64,通用x86-64CPU,不知道的情况下,可以选择这一项
回退上级菜单,选择Symmetric multi-processing support ,CPU对称多处理器的支持(多核处理器)
然后是总线的配置,Bus options (PCI etc.) ---> ,对PCI的配置,进入子菜单:
PCI support选中,会多出很多选项,默认就可以。
Device Drivers ---> 设备驱动的配置,主要是硬盘驱动的配置
lspci命令查看我们的存储设备,是:SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)
选择:Fusion MPT device support ---> 的子菜单中对应我们自己的硬盘设备,但是子菜单中没有列出来:
因为是SCSI总线,先要启用SCSI特性,所以选择: SCSI device support ---- 这一项启用,但是这一项进去全是空的:
需要返回上一级,选择:Enable the block layer ---- ,然后下面的scsi特性才能出来。
不做选择,采用默认。然后回到Device Drivers --->菜单,此时SCSI device support ---> 下显示:
选择: SCSI device support ,会列出很多选项:
选择:SCSI disk support,退出,再次进行硬盘驱动的选择:
选择: Fusion MPT ScsiHost drivers for SPI 这一项。为了保证编译后运行正常,,将Fusion的其他三项都选上。
以上配置完后,就可以进行编译。
5、执行编译:make -j 4 bzImage ,编译成bz格式的内核。运行中,提示gcc版本过低,网上查询需要gcc4.8以上,而CentOS6是gcc4.4,所以下载低版本内核试一下:linux-3.19.tar.xz
其menuconfig配置项有所不同:
总的配置就是:先配置内核是64位的,然后配置动态加载模块支持,在其子菜单下选择可以动态卸载,其后配置块设备支持,再配置CPU,支持多核和选择CPU类型,再配置总线选项,配置PCI支持选项,最后设备驱动配置,先配置SCSI设备,支持SCSI disk,即scsi磁盘,然后是硬盘设备配置,这里是Fusion MPT device。这样一个最基本的Linux内核就算配置完成。
执行编译:make -j 4 bzImage 最后的结果显示如下:
生成的内核位置:arch/x86/boot/bzImage
6、配置新linux的安装位置,需要新增一块硬盘,将自定义的Mini Linux安装在这块硬盘上:
关闭当前系统,进行虚拟机设置:
添加后两块磁盘。启动系统。
7、查看磁盘信息,对磁盘进行分区和格式化
查看磁盘:fdisk -l /dev/sdb
对磁盘进行分区,对一个基本Linux,分区一般最少3个:boot分区,根(/)分区和swap分区,对于最精简的linux,swap可以不使用,所以,指定boot和根/分区就可以了。
进行分区格式化:mke2fs -t ext4 /dev/sdb1 、 mke2fs -t ext4 /dev/sdb2
8、挂载创建的分区到当前系统上。
mkdir /mnt/{boot,sysroot}
mount /dev/sdb1 /mnt/boot/
9、安装grub:grub-install --root-directory=/mnt /dev/sdb
这里要注意的关键点是参数--root-directory=/mnt,而不是/mnt/boot,原因在Linux入门攻坚——16、Linux系统启动流程中分析过。
至此,grub安装完成,下一步复制内核。一般的内核都是/boot/vmlinuz-文件,我们的内核在上一步已经编译完成并生成,即/usr/src/linux/arch/x86/boot/bzImage,将其拷贝到要制作的mini linux的/boot下:cp /usr/src/linux/arch/x86/boot/bzImage /mnt/boot/bzImage
有了内核,还要编辑grub的启动配置菜单,创建/mnt/boot/grub/grub.conf:
关键点是这里的root(hd0,0)和root=/dev/sda2,在当前的机器上,我们的mini linux是安装在第二块硬盘上的,应该是hd1,0 和/dev/sdb2,为何改为了hd0和sda2呢?这是因为mini linux单独运行时,就是单独在第二块硬盘上,此时对mini linux只有一块硬盘,相应的系统就应该识别成第一块硬盘,即hd0,0和/dev/sda2
10、测试启动Mini Linux:
将编译机器关闭,在vmware中新建虚拟机:
启动新建的虚拟机:
启动后,就是一个光标在黑屏上闪烁,于是再下了一个linux-3.10.67版本,重新制作内核,再次运行:
提示没有加载根文件系统。设备已经能够识别了,即sda1和sda2,其上有文件系统(通俗讲就是已经格式化了),只是其上没有对应文件,这里提示不能挂载,是内核驱动不了文件系统。内核中没有添加文件系统模块,需要重新编译内核,使其能够驱动文件系统。设备驱动了,文件系统不能驱动。
11、重新编译内核,添加文件系统驱动。make menuconfig,进入File systems --->选项中
至少保证我们使用的文件系统ext4被选择。然后配置可执行文件格式:Executable file formats / Emulations --->
支持ELF和#!,即bash,配置好后重新编译:
编译后,重新将生成的内核bzImage拷贝到sdb1,即/mnt/boot上,关闭本机,重新启动Mini Linux:
此时显示ext文件系统已经可用,最后是init没有发现。
12、提供一个真正的根文件系统:
启动内核,挂载文件系统后,内核是如何加载init的?查看内核的源代码:init/main.c
可以看到,init的加载,内核依次找的位置为/sbin/init、 /etc/init、 /bin/init 、 /bin/sh,所以,只要存在/bin目录其下有sh,系统就能跑起来。
首先,完成一下根文件系统,现在看所谓根文件系统,就是在根文件系统所在分区下创建对应的文件目录结构:
然后将程序或命令拷贝到对应目录中,不但要拷贝程序文件,还要拷贝命令程序依赖的库文件,于是编写一个拷贝命令程序的脚本文件:
#!/bin/bash
#
target=/mnt/sysroot
[ -d $target ] || mkdir /mnt/sysroot
read -p "A Command:" cmd
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 [ "$cmd" != "quit" ]; do
if ! which $cmd &> /dev/null; then
read -p "No such command,enter again:" cmd
continue
fi
cmd=$(which --skip-alias $cmd)
cmddir=$(dirname $cmd)
[ -d $target$cmddir ] || mkdir -p $target$cmddir
[ -f $target$cmd ] || cp $cmd $target$cmd
libcp $cmd
read -p "Another command(quit):" cmd
done
代码实现的功能,就是在当前系统中,先测试给定的命令是否存在,如果存在,将此命令以及此命令依赖的库文件对等的拷贝到新系统的对应位置,如/bin/ls拷贝到/mnt/sysroot/bin/ls
可以测试一下:chroot /mnt/sysroot/ /bin/bash,将target作为根目录(运行其中的 /bin/sh )
chroot命令 用来在指定的根目录下运行指令。chroot,即 change root directory (更改root 目录)。在 linux 系统中,系统默认的目录结构都是以 / ,即是以根 (root) 开始的。而在使用 chroot 之后,系统的目录结构将以指定的位置作为 / 位置。
在经过 chroot 命令之后,系统读取到的目录和文件将不在是旧系统根下的而是新根下(即被指定的新的位置)的目录结构和文件,因此它带来的好处大致有以下3个:
1. 增加了系统的安全性,限制了用户的权力:
在经过 chroot 之后,在新根下将访问不到旧系统的根目录结构和文件,这样就增强了系统的安全性。这个一般是在登录 (login) 前使用 chroot,以此达到用户不能访问一些特定的文件。
2. 建立一个与原系统隔离的系统目录结构,方便用户的开发:
使用 chroot 后,系统读取的是新根下的目录和文件,这是一个与原系统根下文件不相关的目录结构。在这个新的环境中,可以用来测试软件的静态编译以及一些与系统不相关的独立开发。
3. 切换系统的根目录位置,引导 Linux 系统启动以及急救系统等:
chroot 的作用就是切换系统的根位置,而这个作用最为明显的是在系统初始引导磁盘的处理过程中使用,从初始 RAM 磁盘 (initrd) 切换系统的根位置并执行真正的 init。另外,当系统出现一些问题时,我们也可以使用 chroot 来切换到一个临时的系统。
13、在重新启动Mini Linux前,修改grub.conf,明确告知内核init的具体位置:
重新启动Mini Linux,启动后:
键入回车键无反映,其他按键也没反映,问题是在编译内核时,键盘驱动没有配置上,也就是没有键盘驱动程序,无法使用键盘。于是要重新配置内核选项,将键盘等基本设备的驱动编译进内核。
make menuconfig ,选择Device Drivers ---> 菜单下的 Input device support ---> :
选择: Keyboards --->,将其选中,还可进入子菜单,选择详细键盘型号,默认AT标准键盘就可以。
要支持鼠标,选中:Mouse interface 选项,然后是Mice选项,其下有详细鼠标类型,对于vmware虚拟机,模拟出来的鼠标是usb口的鼠标,所以还要配置usb的驱动:在Device Drivers --->下有USB support --->选项,选中,然后进入下一级选具体usb类型,先看本机的USB类型:
为了以防万一,将各个版本的都选上:
Mice子菜单在配置USB选项前后:
配置好后,再次编译:make -j 4 bzImage
14、再次重启Mini Linux:
可以看到,bash启动了。可以运行内置命令了。但是,正常的Linux启动的是init,可以编写一个init脚本,来模拟init:
添加一些工具程序:
需要修改grub.conf,将init=/bin/bash去掉,或改为init=/sbin/init,或者不修改,在启动时使用e选项,进行手动修改。再次启动时又出现了错误,init启动不成功,有关键的一步没有做,就是编写的/sbin/init没有赋予执行权限,chmod +x /mnt/sysroot/sbin/init,然后再次启动:
此时,init运行成功。但是在/dev下,没有相关设备,要挂载sda1到/boot下,找不到设备
对于/dev下的设备,是由udev发现并创建的,我们内核中没有编译udev。
15、重新配置内核配置选项,将udev编译进内核:make menuconfig,
Device Drivers --->下的Generic Driver Options ---> 选项下
此时/dev下内核识别出的设备都显示出来。