简单操作系统内核——在屏幕上显示字符
引导程序
一般来说机器加电启动后,整个计算机第一个启动的程序就是固化在PC主板上的BIOS固件,它启动之后检测系统参数,如内存的大小、日期和时间、磁盘设备以及这些磁盘设备用来引导的顺序。BIOS寻找用于装载操作系统的指令。装载操作系统的这个程序就是boot loader。Linux系统默认的boot loader就是GRUB(GRand Unified Bootloader),于是PC上电以后系统启动流程如下:
步骤一、下载源代码
在虚拟机(Vmware)中打开terminal,输入命令:git clone https://gitee.com/lmos/cosmos/tree/master/lesson02
如果提示git command没找到,输入命令:sudo apt-get install git
安装git工具。
代码文件如下:
其中,
-
entry.asm:是一段汇编代码,用作GRUB引导调用,关掉中断,设定CPU工作模式,初始化寄存器及C语言运行环境等;
MBT_HDR_FLAGS EQU 0x00010003 MBT_HDR_MAGIC EQU 0x1BADB002 ;多引导协议头魔数 MBT_HDR2_MAGIC EQU 0xe85250d6 ;第二版多引导协议头魔数 global _start ;导出_start符号 extern main ;导入外部的main函数符号 [section .start.text] ;定义.start.text代码节 [bits 32] ;汇编成32位代码 _start: jmp _entry ALIGN 8 mbt_hdr: dd MBT_HDR_MAGIC dd MBT_HDR_FLAGS dd -(MBT_HDR_MAGIC+MBT_HDR_FLAGS) dd mbt_hdr dd _start dd 0 dd 0 dd _entry ;以上是GRUB所需要的头 ALIGN 8 mbt2_hdr: DD MBT_HDR2_MAGIC DD 0 DD mbt2_hdr_end - mbt2_hdr DD -(MBT_HDR2_MAGIC + 0 + (mbt2_hdr_end - mbt2_hdr)) DW 2, 0 DD 24 DD mbt2_hdr DD _start DD 0 DD 0 DW 3, 0 DD 12 DD _entry DD 0 DW 0, 0 DD 8 mbt2_hdr_end: ;以上是GRUB2所需要的头 ;包含两个头是为了同时兼容GRUB、GRUB2 ALIGN 8 _entry: ;关中断 cli ;关不可屏蔽中断 in al, 0x70 or al, 0x80 out 0x70,al ;重新加载GDT lgdt [GDT_PTR] jmp dword 0x8 :_32bits_mode _32bits_mode: ;下面初始化C语言可能会用到的寄存器 mov ax, 0x10 mov ds, ax mov ss, ax mov es, ax mov fs, ax mov gs, ax xor eax,eax xor ebx,ebx xor ecx,ecx xor edx,edx xor edi,edi xor esi,esi xor ebp,ebp xor esp,esp ;初始化栈,C语言需要栈才能工作 mov esp,0x9000 ;调用C语言函数main call main ;让CPU停止执行指令 halt_step: halt jmp halt_step GDT_START: knull_dsc: dq 0 kcode_dsc: dq 0x00cf9e000000ffff kdata_dsc: dq 0x00cf92000000ffff k16cd_dsc: dq 0x00009e000000ffff k16da_dsc: dq 0x000092000000ffff GDT_END: GDT_PTR: GDTLEN dw GDT_END-GDT_START-1 GDTBASE dd GDT_START
-
hello.lds:进行链接调用;
-
install.md:需要将这个文件里的内容复制到GRUB的cfg配置文件中,才能使电脑开机时可以找到我们的Hello OS;
-
main.c:我们Hello OS的主函数,它调用的printf可不是常见的C语言库函数哦,而是我们自己实现的printf!即下面要讲的vgastr.h;
```c #include "vgastr.h" void main() { printf("Hello OS!"); return; } ```
-
vgastr.h:控制计算机屏幕VGABIOS固件程序显示特定字符;
void _strwrite(char* string) { char* p_strdst = (char*)(0xb8000);//指向显存的开始地址 while (*string) { *p_strdst = *string++; p_strdst += 2; } return; } void printf(char* fmt, ...) { _strwrite(fmt); return; }
-
Makefile:利用make工具来实现编译源代码,主要是将entry.asm、main.c、vgastr.h编译并链接。
步骤二、 编译操作系统
流程
Makefile文件中其实已经写出了如何编译我们的操作系统,有兴趣可以看看。
整个编译流程就如图:
编译
安装gcc和nasm。输入命令:sudo apt-get install nasm
和 sudo apt-get install gcc
。
在当前文件路径下打开terminal,输出指令:make all
。
经过编译,我们的文件夹下多了很多的东西,.O属于中间生成的文件,不用理会,重点在于这个HelloOS.bin文件:这个HeloOS.bin文件就是我们的简单操作系统了
安装
先通过以下命令,找到boot目录挂载分区:
df /boot/
我的结果如下:
也就说我的虚拟机中ubuntu16.04的系统GRUB引导是在硬盘的第5分区。
然后打开文件夹中的install.md,复制粘贴到*/boot/grub/grub.cfg中*,install.md主要是加载我们的Hello OS的启动项:
//install.md
menuentry 'HelloOS' {
insmod part_msdos
insmod ext2
set root='hd0,msdos5' #注意boot目录挂载的分区,这是我机器上的情况
multiboot2 /boot/HelloOS.bin
boot
}
注意!df /boot的结果在哪个sda?,set root=‘hd0,msdos?’中的?就填什么。
而后,运行将我们的Hello OS.bin 文件复制到 /boot/ 目录下:
命令 sudo cp HelloOS.bin /boot/
修改GRUB引导显示时间
打开sudo gedit /etc/default/grub,**修改其中的GRUB_CMDLINE_LINUX_DEFAULT为“text”**如下:
然后需要更新grub配置:sudo update-grub
。
重启
选择系统
显示出来的字符内容,可以在main函数里修改。