一、内容

通过 Lab1 中的 bootloader 可以从实模式切换的保护模式,然后再读取磁盘并加载 ELF 文件以加载 OS
操作系统,操作系统能够读入字符并显示到屏幕上,具体内容如下: 练习 1:理解通过 make 生成可执行文件的过程
练习 2:使用 qemu 执行并调试 Lab1 中的软件练习 3:分析 bootloader 进入保护模式的过程
练习 4:分析 bootloader 加载 ELF 格式的 OS 的过程练习 5:实现函数调用堆栈跟踪函数
练习 6:完善中断初始化和处理
Challenge1:增加一用户态函数,能够实现从内核态到用户态,从用户态到内核态的转化
Challenge2:需要实现用键盘实现用户模式内核模式切换

二、目的

操作系统也是一个软件,只是机器在启动时先会运行操作系统,再在操作系统下面再运行其它软件。所以我们需要了解操作系统的启动过程。机器在启动时,先运行 BIOS 用以检测启动盘,然后跳转到
bootloader 的位置运行 bootloader,随后 bootloader 会加载磁盘上的 OS 信息,开始启动 OS 操作系统。通过这次实验我们可以了解到 bootloader 的运行过程并且学会使用 gdb 调试这个过程,以及 CPU
的中断机制和函数调用过程中的堆栈关系。并且还能了解小型操作系统 ucore OS 的编译运行和启动过程, 其中里面包含了一些工具的使用。
计算机原理:
CPU 的编址与寻址: 基于分段机制的内存管理

CPU 的中断机制
外设:串口/并口/CGA,时钟,硬盘
Bootloader 软件:
编译运行 bootloader 的过程调试 bootloader 的方法
PC 启动 bootloader 的过程
ELF 执行文件的格式和加载
外设访问:读硬盘,在 CGA 上显示字符串
ucore OS 软件:
编译运行 ucore OS 的过程
ucore OS 的启动过程调试 ucore OS 的方法
函数调用关系:在汇编级了解函数调用栈的结构和处理过程中断管理:与软件相关的中断处理
外设管理:时钟

三、实验环境配置以及工具说明

实验环境:Linux 64 位用到的工具有:
1、Make,执行 makefile 中的命令,编译 OS 形成.img 文件
2、qemu,用来加载 OS 的硬件模拟器,可以模拟很多架构的硬件环境。
3、GDB,用来调试操作系统的加载过程,可以设置断点并且查看汇编代码。

四、实验步骤以及结果分析

  • 练习 1:理解通过 make 生成可执行文件的过程

1.1 ucore.img 是 如 何 一 步 一 步 生 成 的 ?
make 之后会执行 makefile 里面的指令,先对 ucore 操作系统进行编译,初始化,debug 工具,
以及一系列驱动的编译,之后生成 ucore.img 的命令如下:

即:
dd if=/dev/zero of=bin/ucore.img count=10000 dd if=bin/bootlock of=bin/ucore.img conv=notrunc
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
可以看到,先生成了 kernel 和 bootblock ,最后再生成.img 文件。使用 make “V=” 的指令可以将运行结果进行输出,可以看到

先是对 kern 中的文件进行编译生成一系列.o 文件,而 kern 文件夹下存储着就是 ucore OS 的源码。
生成 bootblock 的过程,查看 makefile 中相关内容

在执行过程中执行了如下指令

对 bootmain.c sign.c 进行了编译,最后通过 ld 进行链接生成了 bootblock
最后再生成.img 文件,我们可以了解在生成.img 文件中相关参数的含义
-if 表示输入文件

-of 表示输出文件
count 表示被赋值的块数conv=notrunc 不截短输出文件执行之后 ucore.img 生成完毕

1.2 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
我们可以进入 sign.c 中,查看关于硬盘主引导扇区的描述

可以看到
磁盘主引导扇区只有 512 字节
最后两个字节为 0x55AA

  • 练习 2:使用 qemu 执行并调试 lab1 中的软件

要想对整个过程进行调试,我们可以在 /tools/gdbinit 文件中更改相关信息,以便再开始调试时加载其中的指令,开始调试。

2.1 设定 i8086 为系统默认结构;设定与 qemu 模拟器链接的 1234 端口号;
2.2 利 用 b *addr 设置断点

此时断点已经设置在了 0x7c00
2.3 输入 c 程序继续进行,此时来到第二个断点 0x7c00,

2.4 输入 x /4i $pc 可以查看当前执行到的位置以及汇编代码

  • 练习 3:分析 bootloader 进入保护模式的过程

我们可以进入/boot/bootasm.S 中查看完整的过程
1.从 0x7c00 地址开始,先进入的实模式(16 位地址访问),此时会禁止中断

2. 然后建立重要的数据段寄存器,DS ES SS ,全部置为 0

3.开启的时候需要等待 8042 Input buffer 为空

4.然后发送 Write 8042 Output Port (P2)命令到 8042 Input buffer

5.然后再等待 8042 Input buffer 为空,8042 Output Port(P2)得到字节的第 2 位置 1,然后写入8042 Input buffer;

6.初始化 GDT 表:一个简单的 GDT 表和其描述符已经静态储存在引导区中,直接载入

7.进入保护模式:此时 cr0 寄存器的 PE 位置置为了 1,因此保护模式开启了

8.跳转之后,基址更新,真正跳转到保护状态

9.最后设置相关寄存器以及堆栈指针

call bootmain 进入保护模式完成

因此我们可以回答以下问题:
3.1 为何开启 A20 和如何开启 A20?
A20 未开启时是实模式,此时访问超过 1MB 的地址时,就会从 0 循环计数,只有开始 A20 进入保护模式时才能完整的访问 4G 内存。
打开过程如下:

  1. 等待 8042 Input buffer 为空;
  2. 发送 Write 8042 Output Port (P2)命令到 8042 Input buffer;
  3. 等待 8042 Input buffer 为空;
  4. 将 8042 Output Port(P2)得到字节的第 2 位置 1,然后写入 8042 Input buffer;

3.2 如何初始化 GDT 表?
一个简单的 GDT 表和其描述符已经静态储存在引导区中,通过 lgdt gdtdesc 载入即可

3.3 如何使能和进入保护模式?
进入保护模式:通过将 cr0 寄存器 PE 位置 1 便开启了保护模式通过长跳转更新 cs 的基地址
设置段寄存器,并建立堆栈
转到保护模式完成,进入 boot 主方法

  • 练习 4:分析 bootloader 加载 ELF 格式的 OS 的过程

4.1 bootloader 是如何读取硬盘扇区的? 流程如下:
1、等待磁盘准备好;
2、发出读取扇区的命令;
3、等待磁盘准备好;
4、把磁盘扇区数据读到指定内存

可以查看相应的代码
Bootloader 读取硬盘扇区:

利用 readseg 函数读取磁盘信息

先四舍五入到磁盘扇区的边界将字节转换到扇区
内核从扇区 1 开始
按递增的顺序开始加载磁盘内容

4.2 bootloader 是如何加载 ELF 格式的 OS? 流程如下:

  1. 读取 ELF 的头部;
  2. 判断 ELF 文件是否是合法;
  3. 将描述表的头地址存在 ph;
  4. 按照描述表将 ELF 文件中数据载入内存;
  5. 根据 ELF 头部储存的入口信息,找到内核的入口(不再返回); 具体代码如下:

读取磁盘后,先判断 ELF 是否有效

先加载每个程序段

进入 ELF 的头指针入口

之后便开始加载 ELF 里面的 OS 系统

  • 练习 5:实现函数调用堆栈跟踪函数
    1、先获取 ebp 和 eip 的值,其中. ebp 指向堆栈位置调用者的 ebp
    2、ebp+4 指向调用者调用时的 eip,ebp+8 是函数的参数
    3、bootloader 设 置 的 堆 栈 从 0x7c00 开 始 , 使 用 “call bootmain” 转入
    bootmain 函数。 call 指令压栈,所以 bootmain 中 ebp 为 0x7bf8
    4、然后通过 ebp+12,ebp+16,ebp+20,ebp+24 来输出 4 个参数的值
    5、最后更新 ebp:ebp=ebp[0],更新 eip:eip=ebp[1],直到 ebp 对应地址的值为 0
    编写的代码如下所示:
  • 练习 6:完善中断初始化和处理

6.1 中断向量表中一个表项占多少字节?其中哪几位代表中断处理代码的入口?
中断描述符表(也可简称为保护模式下的中断向量表)中一个表项占 8 个字节,2-3 字节是段选择子,0-1 字节和 6-7 字节拼成位移(offset),两者联合便是中断处理程序的入口地址

6.2 请编程完善 kern/trap/trap.c 中对中断向量表进行初始化的函数 idt_init
先填写 IDT 表,再将表头地址告诉 LIDT,以便加载 IDT 表

其中 SETGATE 的宏定义如下:

其中有如下定义:
gate:为相应的 idt[]数组内容,处理函数的入口地址
istrap:系统段设置为 1,中断门设置为 0
sel:段选择子
off:为 vectors[]数组内容
dpl:设置特权级。这里中断都设置为内核级,即第 0 级

6.3 请编程完善 trap.c 中的中断处理函数 trap,在对时钟中断进行处理的部分填写 trap
在时钟中断中,每中断一次便将 ticks 累加 1,当加到 100 时,输出并且归零,这样便可以得到相应的功能函数,其中 print_ticks()实现的是打印功能

执行结果:

  • Challenge1:增加一用户态函数,能够实现从内核态到用户态,从用户态到内核态的转化

出于安全考虑,操作系统会将进行用户态和内核态的区分,内核态权限高,可以调用一
些系统级的操作,而用户态权限低,访问的空间和执行的操作受限。因此一些用户级的应用需要调用系统操作时,就必须要陷入操作系统提供的陷阱,只有这样才能从用户态进入内核态进行操作。
/kern/init/init.c 中
lab1_switch_to_user()是用来从内核态切换到用户态的。先需要预留一段 8 字节空间用来给之后 ss 和 esp 的入栈。
lab1_switch_to_kernel()是用来从用户态切换到内核态的。
/kern/init/init.c lab1_switch_to_user()

/kern/init/init.clab1_switch_to_kernel()

从内核态到用户态

从内核态到用户态,需要建立切换到用户态所需的 trapfram 结构的数据 switchk2u
将 tf_cs,tf_ds,tf_es,tf_ss 设置为用户态的代码段和数据段
设置 EFLAG 的 I/O 特权位,使得用户态可以使用 in/out 指令
设置临时栈,指向 switchk2u,这样 iret 返回时,CPU 会从 switchk2u 恢复数据,而不是现有的栈,从而完成内核态到用户态的转变 d
代码如下:

从用户态到内核态

把 tf_cs,tf_ds 等设置成内核代码和内核数据段设置 EFLAGS,让用户态不能执行 in/out 指令
设置临时栈,指向 switchu2k,当 iret 返回时,cpu 会从 switchu2k 恢复数据,而不是从现有的栈恢复数据
代码如下:

利用 make grade 进行验证

得分 40/40

  • Challenge2:需要实现用键盘实现用户模式内核模式切换

我在这采取的是在操作系统上增加一个功能,即是用户态的动作,捕捉键盘中断信号,判断接受的字符,再进行相应的转化操作。即在 while 循环中添加用户功能。
然后在进行验证判断

操作系统启动后输入 0,3 进行验证

结果正确

操作系统实验—ucore Lab1相关推荐

  1. ucore操作系统实验笔记 - Lab1

    最近一直都在跟清华大学的操作系统课程,这个课程最大的特点是有一系列可以实战的操作系统实验.这些实验总共有8个,我在这里记录实验中的一些心得和总结. Task1 这个Task主要是为了熟悉Makfile ...

  2. 中断处理过程示意图_ucore操作系统实验笔记 - Lab1

    最近一直都在跟清华大学的操作系统课程,这个课程最大的特点是有一系列可以实战的操作系统实验.这些实验总共有8个,我在这里记录实验中的一些心得和总结. Task1 这个Task主要是为了熟悉Makfile ...

  3. 操作系统实验Ucore lab8+反馈队列

    综合实验(lab8+反馈队列) 前言 这是中山大学数据科学与计算机学院2019年操作系统实验中关于Ucore的项目以及实验报告,实验要求与Ucore手则有少量出入. 所有源代码已经上传至github. ...

  4. 操作系统实验Ucore:Kernel_init(四)

    本文首发于我的博客 上一节进行到了kernel_init的printf_kernelinfo,继续往下分析 1.pmm_init 这个函数,顾名思义是用来初始化物理内存的函数,这个函数只会调用gdt_ ...

  5. 操作系统实验ucore lab7

    阅读前注意事项: 1.我的博客从lab2之后,如果没有特殊说明,所有标注的代码行数位置,以labcodes_answer(答案包)里的文件为准!!!因为你以后会发现做实验用meld软件比较费时费力,对 ...

  6. 操作系统实验ucore lab6

    阅读前注意事项: 1.我的博客从lab2之后,如果没有特殊说明,所有标注的代码行数位置,以labcodes_answer(答案包)里的文件为准!!!因为你以后会发现做实验用meld软件比较费时费力,对 ...

  7. 操作系统实验ucore lab4

    阅读前注意事项: 1.我的博客从lab2之后,如果没有特殊说明,所有标注的代码行数位置,以labcodes_answer(答案包)里的文件为准!!!因为你以后会发现做实验用meld软件比较费时费力,对 ...

  8. 操作系统实验报告1:ucore Lab 1

    操作系统实验报告1 实验内容 阅读 uCore 实验项目开始文档 (uCore Lab 0),准备实验平台,熟悉实验工具. uCore Lab 1:系统软件启动过程 (1) 编译运行 uCore La ...

  9. ucore lab1 实验报告

    UCORE LAB1 实验报告 练习一 理解通过make生成执行文件的过程 1.操作系统镜像文件ucore.img是如何一步一步生成的? 先打开lab1文件夹下的Makefile,查看里面的代码,在各 ...

最新文章

  1. 数据智能与计算机图形学领域推荐论文列表
  2. automapper
  3. 谷歌最新黑科技:裸眼3D视频通话,宛如真人面对面!Jeff Dean:魔镜啊魔镜
  4. python中可选参数_带可选参数的Python函数
  5. leetcode151. 翻转字符串里的单词
  6. Jquery+asp.net实现Ajax方式文件下载实例代码
  7. mysql管理密码修改及管理权限设定(zz)
  8. 如何理解失效模式与影响分析(FMEA)
  9. 关于C语言中fseek函数的使用
  10. mysql网吧管理系统_网吧管理系统(数据库)
  11. linux时间戳简介,linux 时间戳
  12. Spring动态代理实现
  13. 一举拿下阿里、字节、美团、京东、虾皮offer
  14. android商品销售系统,化工产品销售管理系统
  15. numpy中np.nan(pandas中NAN)
  16. SWUST OJ492: 荷兰国旗问题
  17. Adobe Bridge 2021最新中文版来了!!!!
  18. 不平静,就不会幸福(转)
  19. 2d加速 stm32_emWin做人机用户界面显示刷屏慢? 试试带2D图形加速的GUI图形屏
  20. 视频教程-NB1:物联网入门-物联网技术

热门文章

  1. 【爬虫】利用Python爬虫爬取小麦苗itpub博客的所有文章的连接地址并写入Excel中(2)...
  2. mac os 10.8.5下 微信开发者工具安装问题
  3. tcl/tk参考——列表操作lappend
  4. OneNET平台创建产品
  5. Type-C强光手电快充方案
  6. XTUOJ-1251-Colombian Number
  7. github访问不进去,浏览器证书不安全,访问失败,证书失效,证书颁发者为VMware,谷歌浏览器小bug
  8. 查看google浏览器里的证书
  9. android指南针校准 代码_Android指南针app的实现原理总结
  10. one 主格 复数 宾格_主格、宾格、名词所有格