相信计算机专业的人都学过操作系统相关的知识,也有很多人安装过操作系统,那么我们在学习相关理论知识的时候,有没有真正动手实践过怎么让机器启动就加载我们的代码并将cpu指令进入内核呢。小子不才,根据网上资料踩坑之后成功运行了操作系统版helloworld,下面是一篇踩坑指南。
环境介绍macbookprom1芯片的机器(该配置无视)parallelsdesktop(虚拟机软件)安装ubuntu20.04.2ARM64虚拟机(内置安装)安装bochs(一款模拟操作系统硬件,cpu的软件)免费
sudoaptupdatesudoaptinstallbuild-essentiallibx11-devxorg-devlibgtk2.0-devwgetsourceforge.net/projects/bochs/files/bochs/2.7/bochs-2.7.tar.gztarzxvfbochs-2.7.tar.gzcdbochs-2.7/./configure--enable-debugger--enable-disasm--enable-debugger-guimakesudomakeinstall#./configure后面的参数便是打开调试功能的开关
安装nasm汇编编译器
aptinstallnasmnasm-v#查看是否安装成功
操作系统启动过程
BIOS阶段
计算机在按下开机键后,首先运行的是BIOS程序,BIOS首先进行加电自检,完成对系统的全面检查。确保核心组件如CPU、主板、内存、串并口、磁盘等正常运行。这一检测经常被称为post或poweronselftest。完成加电自检后,BIOS所做的第二项工作就是设备初始化。然后,BIOS将根据用户所指定的引导次序,按照顺序选择引导设备。在选择了合适的引导设备后,BIOS会把引导设备第一个数据块载入内存,并把执行权移交给它。
引导程序加载阶段
第一个数据块所记载的是主引导记录(MBR),共个字节。包括:引导加载程序(BootLoader)(前个字节,如GRUB等)、磁盘分区表(DPT,DiskPartitionTable)、分区有效性标志(55AA)。引导加载程序负责加载启动硬盘分区中的操作系统。
Linux系统中的引导程序有以下3个作用:
编写合适的内核命令行
装载合适的初始虚拟磁盘(简称initrd)
装载合适的linux内核并移交控制权给它。
引导程序在设计上通常包括两个阶段:第一阶段,第一阶段的引导程序通常很小,适合存放在MBR中(因为只有个字节)。第一阶段引导程序的任务是:定位、装载并把控制权传递给第二个阶段的引导程序,这个程序在文件系统中是看不到的;第二阶段,第二阶段的引导程序通常就是引导程序自身,它在启动时就运行了某种形式的应用程序,能够读取有关默认设置的配置信息。第二阶段引导程序通常是文件系统中可以识别的二进制文件。
内核运行阶段
Linux内核文件都是以压缩格式存放的,在上一步骤成功完成后,Linux内核被加载至内存中,内核文件首先完成自解压,并在ramdisk文件的帮助下,内核拥有了访问磁盘及文件系统等基本设备的驱动程序。随后内核在完成探测可识别的硬件设备并加载相应驱动,以只读方式加载根文件系统等操作后,内核就可以启动位于磁盘上的第一个应用程序init,进而完成系统的初始化。
编写bootloader汇编程序
BIOS由Bochs虚拟机提供,我们接下来写的就是这个字节的引导扇区(MBR)的汇编代码。目前它并不用加载操作系统,我们只让它在屏幕上打印出经典的“helloworld”即可。
汇编代码
org0x07c00movax,csmovds,axmoves,axmovax,Messagemovbp,axmovcx,13movax,0xmovbx,0xmovdh,0movdl,0int0x10jmp$Message:db"Hello,world!"times-($-$$)db0dw0xaa55
代码和数据是按汇编程序的编写顺序依次连续存放到内存的,即上面的程序在0x7c00处开始存放的是org0x07c00的机器指令,在字节最后放的是0xaa55数据。
BIOS程序在把引导程序加载到内存时,同时还创建了中断系统,在物理内存的前1KB空间初始化中断向量表,在物理内存最后KB物理地址空间内保存中断处理程序。cpu运行完BIOS后,物理内存的布局如下:
具体代码的含义可以自学汇编语法,此处我也是刚入门
制作虚拟硬盘
#新建bochs项目目录,随意取名mkdirbochs_project#将上面的代码保存到bochs_project目录HelloWorld.asm#使用汇编编译器nasm将该代码编译为可执行文件nasmboot.asm-oboot.bin#然后在本层目录中创建一个大小为1MB的硬盘镜像文件b.img的命令如下:ddif=/dev/zeroof=b.imgbs=count=#dd是文件拷贝命令,其中:#if=/dev/zero:表示拷贝的源文件的路径,"/dev/zero"是一个特殊的文件,可以提供n个0(n的值等于bs和count参数的积)#of=b.img:表示拷贝的目标文件路径。若不存在,则创建该文件bs=:表示大小,单位为B#count=:表示拷贝的文件的块的数量。#由bs和count参数可知,硬盘镜像文件的大小为:*B=1MB,硬盘镜像文件制作好后,将可执行文件boot.bin拷贝到硬盘镜像文件b.img(硬盘)的引导扇区的命令如下:ddif=boot.binof=b.imgbs=seek=0conv=notrunc#其中:#seek=0:表示把可执行文件boot.bin拷贝到硬盘镜像文件b.img的引导扇区(扇区号为0)。#conv=notrunc:表示不改变目标文件的大小,若没有该选项,则硬盘镜像文件b.img的大小会由1MB变为可执行文件boot.bin的大小B。
这样一个写入了引导程序的“硬盘”就制作好了。
bochs使用
"硬盘”制作好后,要想启动bochs还需要一个配置文件——bochsrc.bxrc。为什么需要配置文件呢?因为你需要告诉bochs你希望的虚拟机是什么样的,比如,内存多大,使用哪个硬盘启动等等。在下载bochs的源码包中有一个.bochsrc,就是官方提供的配置文件示例,我们可以根据这个更改。
cp../bochs-2.7/.bochsrcbochsrc.bxrc#拷贝过来后,编辑这个文件找到以下的参数进行修改romimage:file=/usr/local/share/bochs/BIOS-bochs-latestvgaromimage:file=/usr/local/share/bochs/VGABIOS-lgpl-latestata0-master:type-disk,path="b.img"megs:16cpu:count=1boot:disk
其中:
romimage:指定bochs运行过程中使用的ROM-BIOS的路径。
vgaromimage:指定bochs运行过程中使用的VGA的ROM-BIOS的路径。
ata0-master:指定硬盘镜像文件b.img的路径。
megs:指定物理内存的大小,单位为MB。
cpu:指定cpu的个数,1个。这个参数我们只需要count,其他的都可以删掉
boot:指定启动方式,从硬盘启动。
改完这些还是会报错Bochsisnot