前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VV的操作系统笔记(一)操作系统I SeeYou!!!!

VV的操作系统笔记(一)操作系统I SeeYou!!!!

作者头像
Pulsar-V
发布2019-04-17 15:57:49
1.7K0
发布2019-04-17 15:57:49
举报
文章被收录于专栏:Pulsar-VPulsar-V

注:与本系列博客同时同步的还有后面需要学习和研究的FreeRTOS和linux0.11-linux1.0内核代码VV的Linux操作系统内核笔记系列,即使笔者已经自己写了个操作系统了,但是为了能够使博客能读懂,笔者需要把每一个lab和代码打出来做出解释同时笔者也有自己繁重的学习和工作(本科狗),所以进度会非常非常慢

准备工作

Ubuntu16.04-i386 32位操作系统镜像

话不多说,迅雷下载下载地址

  1. 安装镜像到到虚拟机

安装过程不多赘述,安装完成如图所示:

  1. 安装ubuntu的一些软件和包
代码语言:javascript
复制
apt-get install docker docker.io docker-compose qemu virtualbox 
  1. 安装IDE

Eclipse的CDT原生支持Makefile工程,而且虚拟机内存占用较小,所以这里我们就用Eclipse CDT注意是32位的Eclipse表问我为啥安装Eclipse Indigo这种老玩意,因为最新版本的EC并不支持32位。

在我们编写内核的过程中,我们使用GRUB来启动我们的内核。 至于为什么用GRUB,因为它可以设置多系统共存,这样的话你就可以打包多个系统内核同时存在并且启动的镜像文件。

操作系统启动流程

为了直观和形象,我们直接上图

  • BIOS(Basic Input/Output System),基本输入输出系统,该系统存储于主板的ROM芯片上,计算机在开机时,会最先读取该系统,然后会有一个加电自检过程,这个过程其实就是检查CPU和内存,计算机最基本的组成单元(控制器、运算器和存储器),还会检查其他硬件,若没有异常就开始加载BIOS程序到内存当中。详细的BIOS功能,这边就不说了,BIOS主要的一个功能就是存储了磁盘的启动顺序,BIOS会按照启动顺序去查找第一个磁盘头的MBR信息,并加载和执行MBR中的Bootloader程序,若第一个磁盘不存在MBR,则会继续查找第二个磁盘(PS:启动顺序可以在BIOS的界面中进行设置),一旦BootLoader程序被检测并加载内存中,BIOS就将控制权交接给了BootLoader程序。
  • MBR(Master Boot Record),主引导记录,MBR存储于磁盘的头部,大小为512bytes,其中,446bytes用于存储BootLoader程序,64bytes用于存储分区表信息,最后2bytes用于MBR的有效性检查。
  • GRUB(Grand Unified Bootloader),多系统启动程序,其执行过程可分为三个步骤:
    • Stage1:这个其实就是MBR,它的主要工作就是查找并加载第二段Bootloader程序(stage2),但系统在没启动时,MBR根本找不到文件系统,也就找不到stage2所存放的位置,因此,就有了stage1_5
    • Stage1_5:该步骤就是为了识别文件系统
    • Stage2:GRUB程序会根据/boot/grub/grub.conf文件查找Kernel的信息,然后开始加载Kernel程序,当Kernel程序被检测并在加载到内存中,GRUB就将控制权交接给了Kernel程序。

注意!现代操作系统使用了UEFI启动,但是我们现在不说UEFI,请自行忽略

但是这样也需要我们的Boot程序按照Mutileboot 规范来编译内核,才可以被GRUB引导。 按照Mutileboot规范,内核必须在起始的8KB中的(512字节)包含这一个多引导项头(Multiboot header)。 而且,这个多引导项头里面必须有3个4字节对齐的块。

代码语言:javascript
复制
一个魔术块:包含了魔数[0x1BADB002],是多引导项头结构的定义值。
一个标志块:我们不关心这个块的内容,我们简单设定为0。
一个校检块:校检块,魔术块和标志块的数值的总和必须是0。

我的内核启动代码如下: boot.s

代码语言:javascript
复制
.set MAGIC, 0x1badb002;GRUB魔术块
.set FLAGS, (1<<0 | 1<<1);GRUB标志块
.set CHECKSUM, -(MAGIC + FLAGS);校验块

.section .multboot
	.long MAGIC
	.long FLAGS
	.long CHECKSUM
.section .text
.extern kernel_main;导入kernel_main
.extern system_constructors;导入系统构造函数
.global laoder

loader:
	mov $kernel_stack, %esp
	call system_constructors
	push %eax
	push %ebx
	call kernel_main

stop:
	cli
	hlt
	jmp stop

.section .bss
.space 2*1024*1024
kernel_stack:

一些code解释:

  • CLI:将IF置0,屏蔽掉“可屏蔽中断”,当可屏蔽中断到来时CPU不响应,继续执行原指令
  • STI:将IF置1,允许“可屏蔽中断”,中断到来转而处理中断
  • HLT:本指令是处理器“暂停”指令。
  • JMP:命令跳转指令
  • .global .global 用来让一个符号对链接器可见,可以供其他链接对象模块使用。 .global boot 让_start符号成为可见的标示符,这样链接器就知道跳转到程序中的什么地方并开始执行。linux寻找这个 bootbootbootstart标签作为程序的默认进入点。 在汇编和C混合编程中,汇编程序中要使用.global伪操作声明汇编程序为全局的函数,意即可被外部函数调用,同时C程序中要使用extern声明要调用的汇编语言程序。
  • .extern .extern XXXX 说明xxxx为外部函数,调用的时候可以遍访所有文件找到该函数并且使用它。
  • .long MAGIG .long指示声明变量占用空间,占32位
  • .set 给一个全局变量或局部变量赋值

现在建立符号链接来Link我们的所有object文件 linker.ld

代码语言:javascript
复制
ENTRY(boot)
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386:i386)
SECTIONS {
	. = 0x0100000;
	.text :{
		*(.muiltboot)
		*(.text*)
		*(.rodata)
	}
	.data :
	{
		start_ctors = .;
		KEEP(*(.init_array ));
		KEEP(*(SORT_BY_INIT_PRIORITY( .init_array.* )));
		end_ctors = .;
		
		*(.data)
	}
	.bss :
	{
		*(.bss)
	}
	/DISCARD/ : {
		*(.fini_array*) *(.comment)
	}
}

Makefile 没什么好说的,Makefile负责C/C++的 编译依赖过程

代码语言:javascript
复制
GCCPARAMS = -m32 -W -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-builtin -fno-rtti -fno-exceptions -fno-leading-underscore
ASPPARAMS = --32
LDPARAMS = -melf_i386
GCC = g++
ASM = as
LINKER = ld
CFLAGS = -o $@ -c $<
ASMFLAGS = -o $@ $<
LINKERFLAGS = -T $< -o $@ 

objects = boot.o kernel.o

%.o: %.c
	$(GCC) $(GCCPARAMS) $(CFLAGS)
	
%.o: %.s
	$(ASM) $(ASPPARAMS) $(ASMFLAGS)
	
kernel_lab.bin: linker.ld $(objects)
	$(LINKER) $(LINKERFLAGS) $(objects)
	
all: kernel_lab.bin
	echo "build successed"
clean: 
	rm -rf *.o
	rm -rf *.out
	rm -rf iso
	rm -rf *.iso
	rm -rf *.bin
	
rebuild: clean all
	echo "rebuild"
	
install: kernel_lab.bin
	sudo cp $< /boot/kernel_lab.bin

kernel_lab.iso: rebuild
	mkdir iso
	mkdir iso/boot
	mkdir iso/boot/grub
	cp kernel_lab.bin iso/boot/
	cp boot/grub.cfg iso/boot/grub/grub.cfg
	grub-mkrescue -o $@ iso
	rm -rf iso
	
kernel_vm: kernel_lab.iso
	(killall virtualbox) || true
	virtualbox -startvm "kernel_lab" &

下面是操作系统的主要程序,我们由C++编写,用extern "C"导出我们的函数符号 kernel.cpp

代码语言:javascript
复制
#include "kernel.h"
//因为我们的操作系统没有TTY IO,所以我们需要重新写一个printf函数
extern "C" void  printf(char *str){
	u_short *monitor_io_memory=(u_short *)0xb8000;//注意!重点来啦!0xb8000内存地址是显示器地址,往这里写数据就直接能够输出到屏幕上
	for(int i=0;str[i]!='\0';++i){
		//写入字符串,取或0xff00的意思是我们需要把屏幕高四位拉低,否则就是黑色的字体,黑色的字体黑色的屏幕是啥也看不到的
		monitor_io_memory[i]=(monitor_io_memory[i] & 0xff00) | str[i];
	}
}
//操作系统构造函数委托方法
typedef void(*constructor)();
//全局定义构造委托
constructor start_ctors;
//全局定义析构委托
constructor end_ctors;

//轮询函数,并且执行
extern "C" void system_constructors(){
	for(constructor* i=&start_ctors;i!=&end_ctors;i+=1){
		(*i)();
	}
}
//操作系统主启动函数,这里我们打印一个字符串然后让操作系统进入等待
extern "C" void  kernel_main(const void *multiboot_structure,u_int magicnumber){
	printf("Hello Pulsar-V");
	while(1);
}

别忘了/boot/下的grub.cfg文件,这个是GRUB的配置文件,负责在启动器中列出我们需要启动的内核列表 grub.cfg

代码语言:javascript
复制
set timeout=10 #超时时间
set default=0 #默认启动项
menuentry "PulsarV's OS"	{
	multiboot /boot/kernel_lab.bin
	boot
}

现在,来看看我们的工程目录结构

在Ubuntu16.04的grub生成.iso镜像文件的时候如果出现

代码语言:javascript
复制
grub-mkrescue: warning: Your xorriso doesn't support `--grub2-boot-info'. Some features are disabled

的时候,就通过

代码语言:javascript
复制
sudo apt-get install xorriso

来安装好你的xorriso 然后通过

代码语言:javascript
复制
grub-mkrescue -o kernel_lab.iso iso

来打包我们的操作内核 接下来要做的事情就是控制台下输入编译命令先运行一下最基本的kernel

代码语言:javascript
复制
make kernel_vm

现在是show time! 首先看到的是我们的GRUB启动界面

接下来按下回车或者等待10s

大功告成,现在可以看见了我们的操作系统的Hello World了。

后记

如果启动失败了,就用压缩文件(Ubuntu归档管理器)的形式打开iso文件,检查你的目录结构和GRUB Config

参考文档: [Grub配置文件]http://www.jinbuguo.com/linux/grub.cfg.html [MIT公开课]https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-828-operating-system-engineering-fall-2012/ [计算机操作系统第四版]

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 准备工作
    • Ubuntu16.04-i386 32位操作系统镜像
    • 操作系统启动流程
    • 后记
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档