老李教你写操作系统 0x02 - Hello World v2
大约 2 分钟
目标
今天的目标:规范化开发流程。
在上一篇文章中,出于简洁的考虑只使用了一个.c
文件来做说明。后续的开发中代码会越来越多,一个文件肯定是不够的,今天我们将代码做一下简单的拆分,以便更好的扩展。
代码拆分
先把之前的源文件kernel.c
贴出来:
asm(".long 0x1badb002, 0, (-(0x1badb002 + 0))");
unsigned short *video_buffer = (unsigned short *)0xb8000;
char *message = "Hello, world!";
void kernel_main(void)
{
for (int i = 0; i < 80 * 25; i++)
{
video_buffer[i] = (video_buffer[i] & 0xff00) | ' ';
}
for (int i = 0; message[i] != '\0'; i++)
{
video_buffer[i] = (video_buffer[i] & 0xff00) | message[i];
}
while (1)
;
}
在第1
行我们内联了一行汇编代码来说明我们的kernel
是符合multiboot
规范的,这部分需要提取出去,新建文件boot.S
,内容如下:
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
#define MULTIBOOT_HEADER_FLAGS 0
#define MULTIBOOT_HEADER_CHECKSUM -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
.section .text
.globl entry
.long MULTIBOOT_HEADER_MAGIC
.long MULTIBOOT_HEADER_FLAGS
.long MULTIBOOT_HEADER_CHECKSUM
entry:
hlt
因为我们将这部分内容和内核主要代码拆分开了,所以需要从这段代码中调用内核主函数,将控制权转移到内核。处理器进行函数调用需要栈的支持,所以我们还需要准备一个栈。添加相关代码如下:
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
#define MULTIBOOT_HEADER_FLAGS 0
#define MULTIBOOT_HEADER_CHECKSUM -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
#define STACK_SIZE 0x4000 // 16KB
.section .text
.globl entry
.long MULTIBOOT_HEADER_MAGIC
.long MULTIBOOT_HEADER_FLAGS
.long MULTIBOOT_HEADER_CHECKSUM
entry:
// 初始化栈指针
movl $(stack + STACK_SIZE), %esp
call kernel_main
hlt
.section .bss
.comm stack, STACK_SIZE
内核代码重构如下:
typedef unsigned short uint16_t;
void cprintf(char *str)
{
static uint16_t *video_buffer = (uint16_t *)0xb8000;
for (int i = 0; str[i] != '\0'; i++)
{
video_buffer[i] = str[i] | 0x0f00; // 黑底白字
}
}
void kernel_main()
{
cprintf("Hello kernel!");
}
内核主函数为kernel_main
,由boot.S
调用。
构建过程
之前我们是手动编译、链接和启动虚拟机的,每次修改代码之后都这么操作一遍显然是不科学的。今天我们使用make
来自动化构建过程,对应的Makefile
如下:
objects = boot.o kernel.o
kernel.elf: $(objects)
ld -m elf_i386 -e entry -Ttext 0x100000 $(objects) -o $@
%.o: %.c
cc -m32 -c $<
%.o: %.S
cc -m32 -c $<
run: kernel.elf
qemu-system-i386 -kernel kernel.elf -monitor stdio
clean:
rm -f *.elf *.o
现在,我们只需要敲个make run
就可以完成代码的编译链接并启动虚拟机查看结果了。
完整的代码在这里。