前言

最近接了一个外包项目再加上填一些之前立的flag,发现好像很久没有发博客了。现在编译原理操作系统算法方面都还有大坑没有填,加上离实习越来越近,应用层方面的学习也要加强了,但反倒是压力越大越想摸鱼

之前暑假的时候跟着书上写过一个玩具操作系统,然后之后还翻了一下Linux0.11一起写了一个系列的博客,但是还是觉得认识的太浅薄,然后最近发现清华大学的一个操作系统课程,就跟着了。准备用博客来记录一下,虽然会和之前的那个系列有些重复,但是也算是加强巩固了

BIOS

对于所有寄存器开机时都有各自的初始值,在X86中,CS和EIP寄存器分别是F000H,0000FFF0H,所以X86开机执行的第一条指令就是位于这个位置的指令,一般这条指令都是长跳转指令,从这里跳到BIOS程序的起始位置。

BIOS的工作是硬件自检和初始化,并且读取该设备的第一扇区,并且转交CPU控制权

bootloader

由BIOS加载的第一扇区一般放的都是bootloader,bootloader的工作就是

  • 切换到保护模式,启用分段机制
  • 读取磁盘中的操作系统内核
  • 将CPU控制权转交到操作系统

实模式

实模式的存在主要是为了兼容老的操作系统(16bit),在这种状态下软件可访问的物理内存空间不能超过1MB,主要的区别就是实模式的寻址能力和方式,实模式将整个物理内存看成分段的区域,程序代码和数据位于不同区域,操作系统和用户程序并没有区别对待,而且每一个指针都是指向实际的物理地址。

段基址 << 4 + 段内偏移

所以对于32位的处理器就有了保护模式。bootloader的一个工作就是将实模式切换为保护模式

保护模式

在保护模式下,对内存的管理就采用了分段机制,当然在后面还会有分页机制

分段机制将内存划分成以起始地址和长度限制这两个二维参数表示的内存块,这些内存块就称之为段

段机制里会有四个概念:

  • 段选择子

在实模式下,段寄存器原本存放的是段基址,但在保护模式下存放的是段选择子,段选择子的本质其实是一个索引,在全局描述符表的索引,但是它还包含其它信息,比如特权级信息

  • 全局描述符表

全局描述符表,其实就是一个保存着段描述符的数组

  • 段描述符

至于段描述符就是真正描述物理地址的数据结构了,每个段由如下三个参数进行定义:段基地址(Base Address)、段界限(Limit)和段属性(Attributes)。段属性里包含了一个特权级信息等等

  • 特权级

至于保护模式为什么叫做保护模式,可能就是因为它指定了代码都是在一个特定的特权级下运行的,从而决定了代码可以做什么,不可以做什么

  1. CPL:当前特权级(Current Privilege Level) 保存在CS段寄存器(选择子)的最低两位,CPL就是当前活动代码段的特权级,并且它定义了当前所执行程序的特权级别)
  2. DPL:描述符特权(Descriptor Privilege Level) 存储在段描述符中的权限位,用于描述对应段所属的特权等级,也就是段本身能被访问的真正特权级。
  • 地址转换

在没有进入到保护模式的时候,对应的地址就是相应的物理地址,但是在启动了保护模式下,逻辑地址--> (分段地址转换) -->线性地址 == 物理地址

bootloader主要代码

void
bootmain(void) {
// 读取第一扇区
readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0); // 判断操作系统内核编译后的文件是不是有效的ELF文件
if (ELFHDR->e_magic != ELF_MAGIC) {
goto bad;
} struct proghdr *ph, *eph; // 加载内核
ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff);
eph = ph + ELFHDR->e_phnum;
for (; ph < eph; ph ++) {
readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
} // 启动内核
((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))(); bad:
outw(0x8A00, 0x8A00);
outw(0x8A00, 0x8E00); /* do nothing */
while (1);
}

中断和异常

操作系统和CPU一起提供了一种能够打断操作系统和应用的正常执行,让操作系统完成相关处理,然后在恢复操作系统和应用的正常执行。在操作系统中,这种机制称为中断机制。

当CPU收到中断(通过8259A完成)或者异常的事件时,它会暂停执行当前的程序或任务,通过一定的机制跳转到负责处理这个信号的相关处理例程中,在完成对这个事件的处理后再跳回到刚才被打断的程序或任务中。

中断向量和中断服务例程的对应关系主要是由IDT(中断描述符表)负责。操作系统在IDT中设置好各种中断向量对应的中断描述符,留待CPU在产生中断后查询对应中断服务例程的起始地址。而IDT本身的起始地址保存在idtr寄存器中。

IDT的代码实现

void
idt_init(void) {
// __vectors中存放的就是所有的中断处理程序
extern uintptr_t __vectors[];
int i;
for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
// 设置中断门
SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
}
// 加载进寄存器中
lidt(&idt_pd);
}

所有的中断处理程序最后都是进行一些寄存器的保存然后最后交由__alltraps调用trap

.globl vector0
vector0:
pushl $0
pushl $0
jmp __alltraps
.globl vector1
vector1:
pushl $0
pushl $1
jmp __alltraps __alltraps:
pushl %ds
pushl %es
pushl %fs
pushl %gs
pushal movl $GD_KDATA, %eax
movw %ax, %ds
movw %ax, %es pushl %esp call trap popl %esp .globl __trapret
__trapret:
popal popl %gs
popl %fs
popl %es
popl %ds addl $0x8, %esp
iret

最后trap调用trap_dispatch根据不同的服务来执行相关代码

void
trap(struct trapframe *tf) {
trap_dispatch(tf);
}

Lab1:bootloader操作系统的启动的更多相关文章

  1. 我是如何学习写一个操作系统(二):操作系统的启动之Bootloader

    前言 今天本来的任务看书和把之前写的FragileOS整理一下,但是到现在还在摸鱼,书也只看一点.后来整理了一下写这个系列的思路,原本的目的是对操作系统原理性的学习和对之前写的一个玩具型操作系统的回顾 ...

  2. 通过从代码层面分析Linux内核启动来探知操作系统的启动过程

    通过从代码层面分析Linux内核启动来探知操作系统的启动过程 前言说明 本篇为网易云课堂Linux内核分析课程的第三周作业,我将围绕Linux 3.18的内核中的start_kernel到init进程 ...

  3. ucore lab1 bootloader学习笔记

    ---恢复内容开始--- 开机流程回忆 以Intel 80386为例,计算机加电后,CPU从物理地址0xFFFFFFF0(由初始化的CS:EIP确定,此时CS和IP的值分别是0xF000和0xFFF0 ...

  4. 操作系统的启动与引导问题 BIOS、UEFI、MBR、GPT

    关于ISO.WIM.GHO三者的正确理解. ISO(Isolation)文件一般以ISO为扩展名,是复制光盘上全部信息而形成的镜像文件. WIM是英文Microsoft Windows Imaging ...

  5. Linux内核剖析——操作系统的启动

    一.总体功能 1.从通电到BIOS跳转 1.1 CPU在通电后,先进入实模式,设置CS=0XFFFF,IP = 0X0000(指向BIOS) 1.2 BIOS进行执行系统监测,并且在地址=0处初始化中 ...

  6. 通过简单的Linux内核启动程序代码窥探操作系统的启动原理

    作者:吴乐  山东师范大学 <Linux内核分析> 孟宁 MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.程序设计与分析 ...

  7. CC3200模块的内存地址划分和bootloader,启动流程(二)

    1. 首先启动内部ROM固化的BOOT,然后这个ROM启动需要使用内存空间0X2000 0000 --- 0X2000 4000共16K的空间.一级BOOT的作用是串口升级和驱动库. 2. 然后是二级 ...

  8. 制作安装Linux操作系统的启动U盘方法其实就这么简单

    安装系统前,我们需要制作系统安装启动U盘(或者DVD,但已经很少使用DVD,所以不再介绍),通过启动U盘来帮助我们安装下载好的Linux系统. 接下来我们来介绍在Windows.Linux和MacOS ...

  9. Linux基础:操作系统的启动

    Centos6: # 1.加电自检(BIOS)# 2.MBR引导(512k)dd </dev/zero >/dev/sda bs=1k count=400 # 3.GRUB菜单(选择系统) ...

随机推荐

  1. Flutter实体与JSON解析的一种方法

    vs code作为编辑器 1. 首先,json对象与字符串的转换是使用json.encode和json.decode的,需要导入import 'dart:convert'; 这里主要的自然不是这个,而 ...

  2. vue中路由拦截无限循环的情况

    router.beforeEach(async (to, from, next) => { if (token) { if (whiteList.indexOf(to.path) != -1) ...

  3. 英语lasurite青金石lasurite单词

    lasurite青金石的蓝色,是希望的颜色. 医药功效:青金石可入药,是世界公认的.青金即青金石,是一种不透明或半透明的蓝色.蓝紫色或蓝绿色的准宝石,主要由天蓝石和方解石组成.青金石色是藏传佛教中药师 ...

  4. JavaWeb Listener之HttpSessionBindListener

    HttpSessionBindListener        监听把自身这个对象绑定到HttpSession对象上.解绑 绑定到HttpSession对象上,其实就是调用session的setAttr ...

  5. PHP redis 常用操作

    //在列表头部插入一个值one,当列表不存在时自动创建一个列表,key1为列表名 $redis->lpush("key1", "one"); //在列表尾 ...

  6. brew安装mongodb报错Error: No available formula with the name 'mongodb'

    原因:MongoDB不再是开源的了,并且已经从Homebrew中移除 #43770 设定:  $ brew tap mongodb/brew 安装: $ brew install mongodb-co ...

  7. The Preliminary Contest for ICPC Asia Shenyang 2019 F. Honk's pool

    题目链接:https://nanti.jisuanke.com/t/41406 思路:如果k的天数足够大,那么所有水池一定会趋于两种情况: ① 所有水池都是一样的水位,即平均水位 ② 最高水位的水池和 ...

  8. django 统计表

    1. 复杂版 统计,通过跨表查询和timedate模块过滤找到 from django.db.models import Count class TongJiView(View): def today ...

  9. python类定义的讲解

    python是怎么定义类的,看了下面的文章大家就会了,不用多说,开始学习. 一.类定义: 复制代码代码如下: class <类名>: <语句> 类实例化后,可以使用其属性,实际 ...

  10. 数据库备份 DBS(Database Backup),知识点

    资料 网址 什么是DBS https://help.aliyun.com/document_detail/59133.html?spm=5176.13685554.103.6.3fa463f9CDwW ...