ZeroOS—第1章—HelloWorld(上)

HelloWorld保平安

作为一名合格的BUG作者,每当踏入一个新的领域时,亲手完成一个HelloWorld项目是必不可少的,既可以了解程序基本结构,又可以给予入坑的鼓励,还可以向上天祈福代码永无BUG,,,编不下去了,咱们进入正题。

x86计算机启动过程简介

本小节内容简要叙述x86(或者说是IBM)计算机的启动过程,内容不必记住,有个大概的印象就可以了。内容如有错误,欢迎斧正。

1.通电阶段。这个就是你按下电源键时的阶段,这个阶段主要就是等各个供电电压稳定,在稳定之前电源会一直使CPU的复位引脚有效,说白了就是不让CPU工作,等所有电压都稳定之后,电源才会将复位引脚置为无效,此时CPU开始执行第一条指令。

2.BIOS阶段。CPU在开机时执行的第一条指令是存储在主板的BIOS芯片中的,这个芯片中存储的程序主要功能是检测并初始化计算机的状态,其实就是看看内存鸭、总线鸭、CPU鸭啥的都好不好,如果检测通过,BIOS会将CPU置为16位实模式,并且将第一个可启动磁盘的第一个扇区(512KB的主引导记录)加载至内存0x7c00的位置并跳转至这里继续执行程序。

3.主引导记录阶段。从这个阶段开始,CPU执行什么代码就可以由我们说了算,主引导记录里有什么指令,CPU就执行什么指令,一般来讲,主引导记录工作都是将操作系统内核加载进内存并跳转至操作系统内核继续执行。

4.操作系统阶段。此时由我们的主角开始表演,操作系统内核开始初始化并继续加载其他组件,等所有必要组件都加载完成且没有问题后,你就会看到登录界面,然后登录成功,然后开始快乐,然后,,,好了各位,本系列文章到此结束,我要去打LOL了,拜~

GRUB的Multiboot规范

看着我那0-20-1的亚索,我突然发现还是写BUG比较快乐,好了我们开始继续学习写BUG。

从上节计算机启动的四个阶段可以看出,从第3个阶段开始我们就可以掌控CPU了,那我们这个HelloWorld就是要从这里开始实现吗?要实现的话的确是可以,但是咱们就是要在第四个阶段实现HelloWorld。原因非常简单,因为汇编程序真的很长,我还是比较喜欢写C代码,而且如果从第三个阶段实现我们的程序,学习的曲线就突然有点陡了,这就失去了HelloWorld那种简单装*的快乐。起始阶段确定了,但是现在就有个问题,我们跳过第三阶段直接到第四阶段,那第三阶段的工作由谁做鸭,此时一位带头大哥高调入场,让我们热烈欢迎——GRUB。

作为在第三阶段久经沙场的大哥,GRUB可以让我们安逸的度过第三阶段,直接将我们放在第四阶段的开头,而且还可以满足我们一些小小的要求,比如设置个保护模式(不懂就略过)、读取个内存信息(也不用管)等等合理的要求。

但话说回来,跟着大哥混是要守大哥的规矩的,这个规矩就是Multiboot规范,只要咱们的内核遵守这个规范,那大哥就可以带着咱们快乐的装*,下面由我来简单介绍将要遵守的规范,其实就是三个长度位四字节且地址是四字节对齐(地址可以整除4)的数:

1.内核代码的起始位置必须是0x1badb002这个魔数。主要就是“内核代码的起始位置”,这个起始位置在哪鸭?是内核文件的开头吗?并不是。要解释这个位置首先就要了解elf格式的可执行文件,问题来了,说好的简单呢?说好的快乐呢?这啥也没写呢就来个没见过的概念,elf可是linux下可执行文件的格式鸭,涉及的那么多内容是说学习就学习的吗?你别说,还真是说学就学的。如果你想现在就对elf有一个详细的了解呢,可以直接搜索elf,有一堆文章对其进行详尽的解释的,如果你只是想了解一下,足够理解这个起始位置的话,那不妨听听我对elf的理解:

简单来讲,elf格式对数据的管理是以节为单位的,而节的区分是以数据的类型为依据的,这个数据类型目前咱们就只分为程序指令和程序数据两种类型,在elf格式的文件中,所有程序指令占一节,节的名字叫".text",程序数据占一节,节的名字叫“.data"。现在有了这两个节的概念就可以解释这个起始位置了,魔数必须在”.text“节的开头,而且".text”节必须在所有节的前面。至于每个节在可执行文件中的位置则由链接脚本决定,链接脚本的概念就需要自行搜索“gcc的编译链接过程”了,或者等我后面写一个关于gcc编译、链接和加载过程的文章咯。

2.第二个数是紧挨着魔数的启动标志。这个标志的主要作用就是为了向大哥提那小小的要求,在大哥将CPU交给内核之前,大哥会根据这个标志将我们所需要的数据都准备好。现在我们不用这个标志,直接置0就可以了,后面用到的时候再说。

3.第三个数是紧挨着启动标志的校验和。要求是校验和加上启动标志再加上魔数等于0,别问为什么,因为GRUB大哥就是这么任性。

好了说了这么多其实就是为了这几行代码:

#boot.S
#Multiboot头,可以通过grub kernel指令加载并通过boot启动

.align 4
.text
multiboot_header:
  #define magic 0x1badb002
  #define flags	0
  .long magic
  .long flags
  .long (-magic-flags)
#内核汇编入口
.global _start
.align 4
_start:
stop:
	jmp stop

其中以“#”开头的是宏命令,这个和C语言中的一样,以"."开头的是AT&T汇编中的伪命令,都很简单,不懂直接搜索即可。代码最后以一个死循环结束,用来保证程序不会跑飞了,同时也可以通过风扇来判断我们的内核是否运行正常。

有了源代码就可以进行编译链接了,这里我们直接贴上Makefile和链接脚本kernel.ld,先看个大概的意思,详细的不懂也没关系,等到讲编译链接时会说的,现在就直接复制粘贴就可以了。

MAKE=make
GCC=gcc
LD=ld
CFLAGS=-m32 -ggdb -gstabs+ -fno-stack-protector -fno-builtin -fno-strict-aliasing -O0 -Wall -fno-pic -nostdinc  -I include
LDFLAGS=-m elf_i386 -nostdlib
QEMU_OPTION= -m 128M
OBJS=\
	boot.o
all:
	$(MAKE) kernel

kernel:$(OBJS) kernel.ld
	$(LD) $(LDFLAGS) -T kernel.ld $(OBJS) -o kernel

boot.o:boot.S
	$(GCC) $(CFLAGS) -c boot.S -o boot.o

run:
	sudo qemu-system-i386 $(QEMU_OPTION) --kernel kernel

debug:
	sudo qemu-system-i386 $(QEMU_OPTION) -S -s --kernel kernel &
	gdb -x gdbinit

clean:
	rm *.o
ENTRY(_start)

SECTIONS
{
	. = 0x100000;
	.text :
	{
		*(.text);
		. = ALIGN(4K);
	}
	.data :
	{
		*(.data);
		. = ALIGN(4K);
	}
}

好了现在万事俱备,只差编译了,直接在命令行运行“make"即可得到kernel,也就是我们的内核。如果没有得到内核,参考错误信息进行修改就可以了。

现在我们有了一个极其简单的内核,是不是很容易啊(相对于编写完整的内核来说),然后我们就要看看这个极简内核是不是符合GRUB大哥的要求了,你可以直接通过“make run”命令进行运行,如果qemu没有显示任何错误信息而且你的计算机风扇开始加速的话(因为内核的最后是一个死循环),恭喜你,你成功的编写了一个符合GRUB的Multiboot规范的内核。这个意义可是非常重大的,因为Linux内核可以用GRUB加载运行,你的内核也可以用GRUB加载运行,那岂不是说你的内核和Linux一样咯(一本正经的胡说)。

除了直接运行,还可以通过命令

grub-file --is-x86-multiboot kernel
echo $?

提前让GRUB检查一下你的内核是否符合Multiboot规范,如果echo命令输出0,那么你已经通过了GRUB的检查,可以通过qemu运行你的内核了。

虽然还没有在屏幕上输出HelloWorld,但是我们已经跨入了编写操作系统内核的大门不是吗,至于显示HelloWorld则让我们在下一篇中实现吧(我TM也没想到一个HelloWorld能写上下两篇)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值