作者:bobyzhang,腾讯IEG营运开发工程师
0.故事的开始0.1为何和做哪些
近来家里买了对扬声器,我须要一个数字播放器。一凡研究后我看上了volumio()这是一个基于Debian二次开发的HIFI播放器系统,可以运行下x86和覆盆子派上。
我准备让volumio运行在我2009年订购的老爷机电脑上,也让它发挥一点余温热。正常操作是将volumio的系统镜像刷到U盘上,联接笔记本后使用U盘启动系统即可。并且家里没有找到合适的U盘(穷~~),加上前段时间听了朋友关于linux内核的分享,感叹自己对系统的理解不够。为此我决定使用无盘启动volumio顺便研究一下linux启动原理。
目标:无盘启动volumio系统
0.2方案
正常Linux启动流程大体如下:
BIOS启动,完成自检,选择启动硬件
若果是c盘系统读取MBR
从MBR指示,找到GRUB所在分区,加载GRUB显示菜单
加载Linux内核到显存中
执行INIT程序
步入用户界面
因为我须要从网路启动中标麒麟linux,过程会显得复杂一些,主要变化如下
0.3打算工作
无盘启动并不是说完全没有c盘,只是顾客端本身没有c盘,我们须要在远端给机器提供一种文件储存和c盘共享的方案。我这儿选择的是iscsi共享,相比于NFS和samba共享,它更底层,对系统的兼容性更好。
iSCSI借助了TCP/IP作为沟通的渠道。透过两部计算机之间借助iSCSI的合同来交换SCSI命令,让计算机可以透过高速的局域网集线来把SAN模拟成为本地的存储设备。
关于iscsi的配置不是本文重点,这儿就不详尽描述了,要完成iscsic盘的挂载须要接信息。
iscsi服务器地址:我这儿是nas服务的地址192.168.3.5
target名称:这个是服务端拿来分辨目标的,一般一个target服务一个顾客端,并关联一块共享储存,比如:.freenas.ctl:yong-pc.volumio
initiator名称:这个是顾客端名称,拿来告诉服务端谁来恳求了。
1BIOS和UEFI
打算工作做完,我们先来了解一下计算机的启动原理,这儿就要说到BIOS和UEFI,她们是计算机按下电源后最先被执行的程序。
1.1BIOS(BasicInput/OutputSystem)
上个世纪70年代初,"只读显存"(read-onlymemory,简写为ROM)发明,开机程序被刷入ROM芯片,计算机通电后,第一件事就是读取它。这块芯片里的程序称作"基本输入输出系统"(BasicInput/OutputSystem),简称为BIOS。
BIOS程序首先检测,计算机硬件能够满足运行的基本条件,这称作"硬件自检"(Power-OnSelf-Test),简写为POST。硬件自检完成后,BIOS把控制权转交给下一阶段的启动程序。
这时,BIOS须要晓得,"下一阶段的启动程序"具体储存在哪一个设备。也就是说,BIOS须要有一个外部存储设备的排序,排在后面的设备就是优先转交控制权的设备。这些排序称作"启动次序"(BootSequence)。
1.2UEFI(UnifiedExtensibleFirmwareInterface)
不晓得你们是否发觉,这种年早已很难看到BIOS的身影了。
ROM的储存能力有限,BIOS能驱动的硬件类型和数目大大受限。造成大量新硬件未能在PC启动时被加载。最显著就是你没法在BIOS时使用键盘。据悉BIOS的代码历史悠久无法维护。
在2005年年中时侯,包括BIOS供应商、OS供应商、系统制造商以及芯片生产公司在内的行业参与者统一构建了统一的EFI联盟(UEFI,UnifiedExtensibleFirmwareInterface)并在2006年三月发行了UEFI规范2.0。
自此你可以愉快的在PC启动早期使用键盘,甚至像苹果一样加载网路,实现联网下载并安装操作系统。
UEFI的启动流程和BIOS的启动流程不同,因为我2009年订购的老爷机还是BIOS结构,这儿不详尽展开,简单提一下。
2.PXE
回到我的BIOS老爷机,上电自检完成后BIOS根据设置的启动次序应当交棒c盘,并且并且并且这个机器没有硬碟,也没有插入U盘,找不到任何启动设备的BIOS将控制权交给了网卡arm linux 内核启动流程,BIOS光荣离场步入了PXE阶段。
预启动执行环境(PrebooteXecutionEnvironment,PXE,也被称为预执行环境)提供了一种使用网路插口启动计算机的机制。这些机制让计算机的启动可以不依赖本地数据储存设备(如硬碟)或本地已安装的操作系统。
2.1PXE原理
Client向DHCP发送IP地址恳求消息,DHCP返回Client的IP地址,同时将启动文件(如:pxelinux.0)的位置信息(一般是TFTP路径)一并传送给Client
Client向TFTP发送获取启动文件恳求消息,TFTP接收到消息以后再向Client发送启动文件大小信息,试探Client是否满意,当TFTP收到Client发回的同意大小信息然后,即将向Client发送启动文件Client执行接收文件
Client向TFTP发送针对本机的配置信息文件恳求,TFTP将配置文件发回Client,从而Client按照配置文件执行后续操作。
Client会加载启动文件,然后按照配置执行动作。这儿有多重方案进行下一步操作。
2.2iPXE
里面说到了启动文件,普通的pxe启动文件功能有限,一般只能从tftp服务器上获取文件,不支持HTTP合同和其他共享合同,更别说我们要支持的iscsic盘挂载了。这儿推荐一个高档开源pxe启动文件:iPXE()。它支持从HTTP、iscsiSAN、FibreChannelSAN、AoESAN等多种形式启动,甚至还支持无线网卡。据悉它还可以订制一个启动脚本和菜单。
iPXE须要依照自己硬件对应的平台进行编译,编译前须要厘清楚几个要点:
使用如下命令编译(更多细节见:):
git clone git://git.ipxe.org/ipxe.git
make [platform]/[driver].[extension]
Platform支持如下:根据前面说的启动方法、平台、CPU情况选择。
Driver:主要选择支持的网卡驱动类型,通常选ipxe(表示所有支持的网卡,但可能造成生成的启动文件过大,假如过大可以酌情选其它)
Boottype:和启动方法、启动介质有关,参考下表:
编译时添加EMBED={脚本名称}可以关联一个启动脚本。推荐一个大鳄做好的脚本可以直接使用。
我最终命令如下:
git clone git://git.ipxe.org/ipxe.git
cd ./ipxe/src
wget http://boot.netboot.xyz/menu.ipxe
make bin-i386-pcbios/ipxe.pxe EMBED=menu.ipxe
完成以后在/data/ipxe/src/bin-i386-pcbios/ipxe.pxe可以领到最终的启动文件。
2.3DHCP、TFTP配置
怎样配置DHCP和TFTP服务器不是本文重点,假如须要命令行形式配置可以参考这篇文章的前半部份
现在大部份高档路由器或开源路由器固件都外置了DHCP和TFTP配置功能。我家的LEDE路由器配置界面如下。
拷贝之前编译好的ipxe.pxe和menu.ipxe文件到/www/pxe/目录下,并设置网路启动镜像为:ipxe.pxe
配置正确,启动后就可以看见如下选择界面了:
3.分区:MBR和GPT
ipxe完成使命后,即将交棒给c盘,假如你是硬碟启动,可以直接跳过第2部份,直接到这一步。这一阶段系统须要从c盘上找到启动文件并加载。在说怎么找到启动文件前,先要谈谈硬碟是怎样界定区块的,主要有两大方法MBR和GPT。我们先来聊一下机械硬碟的工作原理。
机械硬碟由坚硬金属材料制成的涂以磁性介质的硬碟linux驱动下载,硬碟两面称为大盘或扇面。
假定盘片不动,硬碟旋转,这么盘片都会在c盘表面画出一个矩形轨迹并将之磁化,数据就保存在这种磁化区中,称之为扇区,将每位扇区分段,一个弧段就是一个磁道。一个硬碟可以包含多个扇面,扇面同轴重叠放置,每位大盘扇区数相同,具有相同边长的扇区所产生的圆锥称之为柱面,柱面数与磁头数相等。如右图:
最初的轮询形式称为CHS,所谓CHS即柱面(cylinder),盘片(header),磁道(sector),通过这三个变量描述c盘地址。
3.1MBR
说了如此多还是没说明白究竟计算机如何从c盘上找到引导程序。答案是:它被固定写死在了0柱面,0盘片,1磁道的位置一般是512bytearm linux 内核启动流程,这个位置被称为主磁道(MasterBootRecord,MBR)。
MBR主要包含如下数据:
Bootloader:这部份记录了一段较小引导代码,用于去启动硬碟其他分区位置上更大的引导文件,比如linux操作系统的grub目录。
我们晓得一个硬碟的每位分区的第一个磁道称作bootsector,这个磁道储存的就是操作系统的loader。如上图,第一个分区的bootsector储存着windows的loader,第二个分区放着Linux的loader,第三个第四个因为没有安装操作系统所以空着。至于MBR的bootloader是干嘛呢,bootloader有三个功能:
DiskPartitiontable:这一部份64字节大小被均分为4份,每份大小16字节,每每我们在硬碟上创建出一个新的主分区或则扩充分区时,便会占用1个16字节的大小用于记录这个分区的相关信息(比如起始和截至柱面位置、分区文件系统类型等等)。这就是为何mbr分区模式最多只能有4个主分区的缘由。
MBR的局限:
现在我家的硬碟都4T了,MBR早就不能满足需求了。你也不能怪MBR,虽然人家1983年就提出了,比我的年龄还大。
3.2GPT
为了解决MBR的问题,GPT分区诞生,GPT全称GloballyUniqueIdentifierPartitionTable,也叫GUID分区表,它是UEFI规范的一部份(但这并不是说它只支持UEFI,它也支持BIOS形式的引导)。
GPT分区结构如下:
3.3Bootloader写入
使用dd命令结合hexdump可以输出MBR信息
dd if=~/Desktop/volumio-2.799-2020-07-16-x86.img ibs=512 count=1 | hexdump -C
同样的使用dd命令可以拷贝MBR信息从img文件到化学c盘。(之前我是分分区写入到c盘的,致使MBR信息遗失未能引导)
dd if=~/Desktop/volumio-2.799-2020-07-16-x86.img ibs=512 count=1 of=/dev/sda
也可以使用下载的syslinux中的mbr.bin写入
dd bs=440 count=1 conv=notrunc if=/usr/lib/syslinux/bios/mbr.bin of=/dev/sda //MBR分区表
dd bs=440 count=1 conv=notrunc if=/usr/lib/syslinux/bios/gptmbr.bin of=/dev/sda //GPT分区表
4.引导加载程序:Syslinux和GRUB
前文说到MBR的bootloader主要功能是交棒内核,并且bootloader不会直接拉起linux内核,400K太小,它没有能力将linux内核直接加载到显存。这时须要引导加载程序登场,它的主要目的就是将系统内核镜像和initrd镜像加载到显存并将控制权交给它们。目前常用的有两种Syslinux和GRUB:
对于普通用户来说她们有哪些用呢?它可以提供选单选择Linux内核版本,再者加载程序促使我们可以向Linux内核传递参数。这点很重要,在我的案例中volumio就是通过Syslinux向内核传递启动参数的。
Syslinux早已不支持bios64位系统了,目前使用GRUB2的比较多。因为volumio使用的是Syslinux我没有对GRUB展开研究。
右图是volumio的默认syslinux配置。
这儿指定了imgpart,bootpart的uuid用于挂载分区,imgfile名子用于确定当前真实root分区的文件名,还有loglvevel、USE_KMSG等参数。
5.内核:vmlinuz和initrd
引导加载程序交棒以后系统步入内核引导阶段。这一步会在显存中运行系统内核和根文件系统。以后根目录下的initshell会被调用执行,完成进一步的初始化操作。
5.1vmlinuz和initrd
vmlinuz是可引导的、压缩的内核。“vm”代表“VirtualMemory”。Linux才能使用硬碟空间作为虚拟显存,因而得名“vm”。vmlinuz是可执行的Linux内核。
initrd是“initialramdisk”的缩写。initrd通常被拿来临时的引导硬件到实际内核vmlinuz才能接管并继续引导的状态。initrd字面上的意思就是"bootloaderinitializedRAMdisk",换言之,这是一块特殊的RAMdisk,在载入Linuxkernel前,由bootloader给以初始化,启动过程会优先执行initrd的init程序,initrd完成阶段性目标后,kernel会挂载真正的rootfilesystem,并执行/sbin/init程序。
采用这些分离的方法,致使我们有机会在内核引导阶段做一些我们自己的事情。简单读了volumio.initrd中的initshell发觉它起码做了几件事情:
读取syslinux传递来的环境变量
按照变量决定是否在屏幕复印日志。USE_KMSG参数决定
加载各类内核驱动模块
挂载boot分区
使用fdisk处理c盘,img文件写入c盘后大小不一致,首次启动须要使用fdisk命令调整分区大小
挂载一个imgpart分区,这个不是真正的root分区,这儿面的volumio_current.sqsh文件才是,这样做的目的是便捷系统升级,在系统内替换imgpart分区的volumio_current.sqsh文件即可完成系统升级。volumio_current.sqsh文件名也是通过imgfile参数决定的。
处理volumio_current.sqsh升级问题,发觉有新的volumio.sqsh文件会重命名旧的,之后将新的重命名volumio_current.sqsh
使用overlay形式结合volumio_current.sqsh文件挂载真正的root分区。
执行switch_root命令,重定向新的根分区并执行/sbin/init命令。
5.2initrd编辑
因为linux内核启动后,之前ipxe对应的环境已然退出,因而之前挂载的iscsic盘也难以访问,须要在initrd的initshell中重新挂载iscsic盘。因而我须要在上文的4步骤之前挂载iscsic盘,更改如下:
加载网卡内核驱动
启动网路
启动iscsi顾客端挂载网路c盘。
可以使用如下形式编辑早已生成好的initrd文件。
mount -o loop,offset=1048576 ./wrt/Build/Volumio2.799-2020-09-29-x86.img ./vboot/ //挂载img镜像的boot分区到目录
cp ../vboot/volumio.initrd volumio.initrd.gz //拷贝initrd文件,重命名一下
gunzip ./volumio.initrd.gz //解压gz文件
cpio -ivmd < volumio.initrd //展开initrd文件,在当前目录就可以看到整个rom disk的内容了
vim init //编辑init shell
find . | cpio -c -o > ../volumio.initrd.img //重新打包成新的initrd
gzip volumio.initrd.img
mv volumio.initrd.img.gz volumio.initrd
还有另外一种方案,因为volumio是开源项目,编译volumio的脚本在github开源。我可以编辑编译脚本,直接更改init以后编译成新的initrd文件。
git clone https://github.com/volumio/Build.git
ls -la scripts/initramfs/init-x86
ls -la scripts/x86config.sh
首先处理x86config.sh脚本,我们须要在initrd中添加iscsi顾客端右图中:193-195行安装iscsi顾客端231-232行向initrd中添加iscsi模块
然后处理init-x86,在118行左右的位置,脚本读取了配置在/proc/cmdline中的根目录uuid并在以后挂载c盘。这儿的cmdline就是之前说到的在syslinux阶段向内核传递的参数。所以我们要在挂载c盘前加载网卡驱动、启动网路、启动iscsi顾客端、挂载iscsic盘。
更改如右图:
这儿要说一下ibft这是一种将iscsi配置信息传递到系统的形式,我们在iPxe阶段早已配置网路信息、iscsi服务器地址、iscsitarget等信息了,这儿可以使用ibft直接读取并使用。其实你也可以在这儿再度自动启用DHCP,自动初始化iscsi顾客端。
更改完成后,iscsic盘就可以像正常本地c盘一样被挂载,然后的操作就和正常硬碟安装一样了,正常启动步入volumio系统。
6.init进程
内核引导阶段完成之后,系统会挂载真实的root分区,执行/sbin/init程序初始化系统环境。这一阶段早已和是否网路启动没有关系了,不过启动原理都研究到现今了就顺便一起看一下吧。
/sbin/init会首先确定运行级别,这个配置在/etc/inittab中,通常Linux有7种运行级别(0-6)。通常来说,0是死机,1是单用户模式(也就是维护模式),6是重启。运行级别2-5,各个发行版不太一样,对于Debian来说,都是同样的多用户模式(也就是正常模式)。确定运行级别后会访问/etc/rcN.d(这儿的N就是运行级别)。
这儿的文件都采用“字母S或K+两位数字+程序名”的命名方法。其中S开头的表示在这个级别须要执行start命令,K开头须要执行Stop命令,数字越小越优先执行。系统会依次执行相应的软件和服务,负责用户界面的程序也被启动你就有了X11界面,之后是SSH服务你就可以使用ssh登陆。这样系统就完成了启动。
其实啦现今这些方法早已过时了,目前基本使用systemd形式用systemctl命令管理。篇幅早已很长了,这块有兴趣的朋友自己搜索一下。
7.尾巴7.1其他遇见的问题
syslinux卡死这个问题上面说到了,挂载iscsic盘后ipxe交棒c盘引导,并且就卡死了。
经过好多的google和尝试过后最终发觉,我使用了64位的iPxe引导固件,并且syslinux只有32位版本造成卡死,更换了32位的iPxe固件后解决。
可以启动未能关掉这个问题困惑了我好久,系统可以正常启动,并且在死机或则重启时会关机,键盘没有任何反应并且系统应当还是活的(大小写灯正常切换)只能强制死机退出。经过排查缘由可能是:死机时网路服务会关掉造成网卡关掉,从而造成iscsi云盘断掉。并且此时系统根分区还没有umount造成系统无响应。
我禁用了网路服务的死机关掉,把K0606networking从rc0.d目录中去除就好了。
Airplay服务难以找到Volumio自带shairport-sync服务,手机可以通过airplay链接volumio系统播放音乐,并且在我折腾完之后发觉如何也搜不到。经过排查shairport-sync使用mDns发布组播告诉局域网内的所有设备自己的地址,使用的是avahi-daemon程序。排查日志发觉它启动时没有辨识到网卡。我猜缘由应当是我们的网卡是在内核引导阶段自己拉起的,并不是步入系统后由networking服务拉起的,所以avahi-daemon未能查找到它对应的ip。
我没有找到挺好的解决方案,还好老爷机还有一块无线网卡,最后使用了无线网卡绑定shairport-sync服务。
7.2最终疗效
7.3总结
总结:为了省掉一块U盘,我开始折腾iscsi无盘启动没想到这一折腾就是很久,前后研究了很多资料好好的学习了一下linux的启动原理。
实际过程并没有文中诠释的这么顺利,好多研究的弯路没有在文中一一诠释下来。在不同的节点也有好多方案可以选择,例如:iPxe本可以直接http下载vmlinuz和initrd引导,这样就可以省去MBR和syslinux引导。并且后来想想都研究了还是整理给你们。再例如initrd中iscsi顾客端的启动和初始化有好多种方法,一开始我都自动初始化网卡,设置dhcp和ip路由。最后还是认为太麻烦发觉ibft的方案最简单,果断选择了它。
水平有限假如发觉哪里总结的不对欢迎见谅。
你都听到这了点个赞再走吧~对了前几天99公益日同学10块钱买了块U盘似乎挺香的~
参考文献
计算机是怎样启动的?
UEFI引导与BIOS引导在原理上有哪些区别?
PXE批量布署安装Linux系统
MBR与GPT
iPXE
MBRvs.GPTGuide:What'sTheDifferenceandWhichOneIsBetter
Syslinux
GRUB
UsingtheinitialRAMdisk(initrd)
Linuxinitrd学习笔记