** ## 操作系统ucore实验——lab1 **

紧急更新实验用的源代码在lab0中的有误改为:

链接:https://pan.baidu.com/s/1RLCG57xDSydH8oQD-JwgPQ
提取码:9i15

中科大的源可能遇见无法使用的情况,换官方的

https://releases.ubuntu.com/focal/ubuntu-20.04.5-desktop-amd64.iso

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

首先打开lab1文件观察一下,然后有makefile,make V=生成make编译过程中详细的过程和参数,和bin文件夹,里面即是编译生成的内核文件夹。ucore.img就在其中。

用Source Insight审计makefile代码

看一下正文,小白肯定看不懂,这个是linux shell的脚本语言,这里推荐大家一本学习linux的一本书《鸟哥的Linux私房菜》图书馆里应该有。那么接下来将对makefile进行一一讲解。

首先学一种语言其实只需要将输入输出,判断循环,结构定义学会基本就掌握七八了。以下是基本符号说明

= 即是简单的赋值
:= 变量不存在或值为空时才对其赋值
echo 输出
num>&num 输出重定向符号0表示标准输入,1表示标准输出,2标准错误输出 1>&2即是将标准输出定向到标准错误输出这里
ifndef…endif 判断是否定义,中间夹带定义内容
if,elif,then 与python类似的then即是if或者elif为真时启用

其余的可看这篇:

https://blog.csdn.net/tangyuanzong/article/details/78595854?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166609720716800184180982%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=166609720716800184180982&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-78595854-null-null.142v59pc_rank_34_2,201v3control_2&utm_term=ucore%20lab1&spm=1018.2226.3001.4187

可以看出ucore是基于qemu开发的操作系统

问题1:操作系统镜像文件ucore.img是如何一步一步生成的?(需要比较详细地解释Makefile中每一条相关命令和命令参数的含义,以及说明命令导致的结果)

make V=(对每条命令进行了精简)后的内容:
如果没有则说明你以前编译过但没清除缓存解决方法:

方法1.
make clean #清除上一次make命令生成的文件
make distclean #清除上一次make以及configure命令生成的文件
方法2.
将文件删除,重新下一个

开始分析:
第一步

  1. +cc kern/init/init.c #编译初始化代码init.c

    • gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.o #编译方法这里大概就是对-kern/init/文件目录下开了保护 可调试 栈保护等等一系列操作…详情请看gcc说明手册,gcc --help即可

第二步

2.+ cc kern/libs/stdio.c #编译输入输出文件,这里并不是BIOS,BIOS是放在ROM固定的,是什么输入输出以后到源码分析会说。
gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/stdio.c -o obj/kern/libs/stdio.o

第三步

  • cc kern/libs/readline.c #编译readline.c文件
    gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/readline.c -o obj/kern/libs/readline.o

第四步

  • cc kern/debug/panic.c#编译panic.c文件
    gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/panic.c -o obj/kern/debug/panic.o

第五步

  • cc kern/debug/kdebug.c #编译kdebug.c文件
    gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kdebug.c -o obj/kern/debug/kdebug.o

第六步

  • cc kern/debug/kmonitor.c #编译kmonitor.c gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kmonitor.c -o obj/kern/debug/kmonitor.o

…此处开始一个文件夹举例说明
第七步

  • cc kern/driver/clock.c#编译时钟代码clock.c
    gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/clock.c -o obj/kern/driver/clock.o


第十一步

  • cc kern/trap/trap.c #编译陷门中断代码
    gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trap.c -o obj/kern/trap/trap.o

第12步

  • cc kern/trap/vectors.S #编译终端向量的汇编代码
    gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/vectors.S -o obj/kern/trap/vectors.o

第14步

  • cc kern/mm/pmm.c #编译pmm.c文件
    gcc -Ikern/mm/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/mm/pmm.c -o obj/kern/mm/pmm.o

数不来了…

ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o obj/kern/libs/stdio.o obj/kern/libs/readline.o obj/kern/debug/panic.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/picirq.o
obj/kern/driver/intr.o obj/kern/trap/trap.o obj/kern/trap/vectors.o
obj/kern/trap/trapentry.o obj/kern/mm/pmm.o obj/libs/string.o
obj/libs/printfmt.o #动态链接生成bin/kernel

  • ld bin/bootblock //根据sign规范生成bootblock
    ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00
    obj/boot/bootasm.o obj/boot/bootmain.o
    -o obj/bootblock.o

    //创建大小为10000个块的ucore.img,初始化为0,每个块为512字节 dd if=/dev/zero of=bin/ucore.img count=10000
    //把bootblock中的内容写到第一个块 dd if=bin/bootblock of=bin/ucore.img conv=notrunc
    //从第二个块开始写kernel中的内容 dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc

通过分析,不仅仅可以在makefile中找到编译命令,并且可以得到ucore.img的生成过程为

1.将所有源代码编译为可执行文件,生成产生bin/kernel所需文件
2.链接生成bin/kernel
3 编译bootasm.S bootmain.c sign.c
4.根据sign规范生成obj/bootblock.o
5.创建10000个块的ucore,将bootblock写入第0块,然后将bin/kernel写入后面的块

问题2:一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
这里找了一下想了想,既然是起规范作用的,那么在前面说了sign可以规范bootblock。那么这片代码一定在sign.c里面。
果然:

则符合符合规范的硬盘主引导扇区的特征

1.扇区大小为512字节
2.多余空间为0
3.第510字节=0x55
4.第511字节=0xAA

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

1.从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行

首先我们看看在用qemu启动内核时我们给内核埋下的调试断点在lab1/tools/gdbinit文件内

意思就是说,我在文件 bin/kernel执行时开一个端口1234给别人交互,并在kern_init函数下断点,运行继续。我们知道CPU加电后是先启动BIOS的那么这个断点是不能看见的所以修改为。

set architecture i8086
target remote :1234
就行了,方便我们将来的调试

然后这里用qemu拉起ucore并设为可调试

qemu-system-i386 -S -s -hda ./bin/ucore.img -monitor stdio
或者
qemu -S -s -hda ./bin/ucore.img -monitor stdio


1.本地调试法
新启一个终端,在lab1文件下

make debug#启动调试

然后输入:

si #单步跟踪
x /20i $pc #查看20次pc程序计数器所指的内容

2.远程调试法
新起一个终端,然后启动gdb
gdb
target remote ip+端口(本地用ip127.0.0.1)
命令

这样就可以让别人帮你远程调试了,也意识到这其实是一个很危险的后门,如果我隐藏在其他不知名文件里面,你的系统将随时被我干掉。

并且第一个断点是0x0000fff0即BIOS的启动点,可看见是成功的。

2.在初始化位置0x7c00设置实地址断点,测试断点正常。

这里将前面启动的全部关闭,然后重新拉起一下ucore.img让他以BIOS开始

新起一个终端,然后本地或者远程调试。
然后设置断点在0x7c00处,然后运行查看程序计数器

b *0x7c00
c
x /20i $pc

由此可知改断点是正常的

这里就可以得到结论,在这个内存空间中BIOS和加载程序的内存分布的分部是,
————————————— <-4G
| 空闲空间 |
—————————————<-1MB=0xffff0
|BIOS固件 |
—————————————<-640KB
|… |
—————————————
|内核加载程序(bootloader) | <-第一块
—————————————<-0x7c00 CS:IP=0000:7c00
|BIOS数据块 |
——————————————0x0

3.从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较
20个20个的看
调试的

bootasm.S的

bootblock.asm

结论:都是一样的
那么他究竟干了什么事呢?这里就直接给出分析的结果,具体细节请大家自己通过汇编来理解。
汇编指令大全:

https://blog.csdn.net/linlibest/article/details/8904624

1.终端处理,然后启动bootloader(512字节)
CLI 清中断允许位(防止在启动的时候被终端打扰)
CLD 清方向标志位
然后进行一系列的寄存器赋值,即是处理系统中断,保存了ds,es,s等,然后跳转执行。

2.等待磁盘扇区,读取磁盘

3.进入保护模式,启动系统,然后用局部内存表(LDT)


至此我们就可以知道,操作系统启动的完全步骤是

1.启动BIOS,将bootblock写入第一块
2.将权限转到bootblock,然后写入操作系统
3.将权限转到操作系统,启动

此时的全局内存表(GDT)
————————————— <-4G
| 空闲空间 |
——————————————
|操作系统 |
—————————————<-1MB=0xffff0
|BIOS固件 |
—————————————<-640KB
|… |
—————————————
|内核加载程序(bootloader) | <-第一块
—————————————<-0x7c00 CS:IP=0000:7c00
|BIOS数据块 |
——————————————0x0

这也是为什么电脑的运存4G,而使用时最多是3G的原因,操作系统自己就占了1G

4.自己找一个bootloader或内核中的代码位置,设置断点并进行测试

随便搞

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

看来不得不细讲bootasm.S了

1.为何开启A20,以及如何开启A20
初始时A20为0,访问超过1MB的地址时,就会从0循环计数,将A20地址线置为1之后,才可以访问4G内存。A20地址位由8042控制,8042有2个有两个I/O端口:0x60和0x64。(A20即是address 20(地址线),他的空间4G)

打开流程:

等待8042键盘控制不忙,即输入字符为空;
发送Write 8042 Output Port (P2)命令到8042 Input buffer;
等待8042 Input buffer为空;
将8042 Output Port(P2)得到字节的第2位置1,然后写入8042 Input buffer;


2.如何初始化GDT表
1.载入GDT

lgdt gdtdesc

2.进入保护模式
通过将cr0寄存器设为1,即进入保护模式

movl %cr0, %eax #将cr0给eax
orl $CR0_PE_ON, %eax #给eax赋值1
movl %eax, %cr0 #给cr0赋值1

3.通过长跳转更新cs的基地址:
上面已经打开了保护模式,所以这里需要用到逻辑地址。$PROT_MODE_CSEG的值为0x8

跳转到protcseg这。

4 设置段寄存器,并建立堆栈

可以看到的是此时DS数据寄存器,ES扩展寄存器,FS标志寄存器,GS全局寄存器,ss堆栈寄存器。都初始指向0x0
具体作用:

https://blog.csdn.net/u011488769/article/details/119488993

esp栈顶指针指向0x7c,ebp指向0x0
5.转到保护模式完成,调用bootmain函数

3.如何使能和进入保护模式
将cr0寄存器置数为1

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

1.bootloader如何读取硬盘扇区的?
在前面已经说过,当控制权给到bootloader时,进入保护模式后,bootloader会将操作系统写到BIOS上面
那么bootloader肯定是将硬盘扇区已经编译生产的OS,进行读取,怎么个读取法,就在bootmain.c中的readsect()函数

这里了解一下这个PC的I O端口地址表 (I/O端口地址表)

https://www.cnblogs.com/chulia20002001/archive/2011/01/11/1932772.html

我们知道我们是使用qemu拉起来的操作系统,它默认操作系统存放在0号硬盘中。(有错误,打算问完老师后修改)
outb的定义:

void outb (uint16_t port , uint8_t data);#内嵌汇编读/写数据,x86外设读取方式

然后用readseg()函数封装,就可以从设备读/写任意长度的内容。

以上就是bootloader如何读/写硬盘扇区的。

2.bootloader是如何加载ELF格式的OS?

在文件elf.h中找到efl的定义:

然后直接看bootmain的读取

所以步骤是:

1.从磁盘里读去8个扇区存到内存0x10000处(刚好在BIOS上面),并强制转换为elf格式使用
2.检验幻数
3.根据偏移量分别把程序段的数据读取到内存中

练习5:实现函数调用堆栈跟踪函数

这里分别使用x86.h中的read_ebp()函数,和kdebug.c中的read_eip()函数

这样函数print_stackframe就是将这些信息输出就行
然后在lab1中

make qemu #就可得到一下结果

#这里的具体分析…打算弄明白了再说.

练习6:完善中断初始化和处理

1.中断描述符表(也可简称为保护模式下的中断向量表(IDT))中一个表项占多少字节?其中哪几位代表中断处理代码的入口?

在mm/mmu.h查看中断向量表的定义

中断机制

中断向量表项

看代码将所有数加起来64位=8字节

2.请编程完善kern/trap/trap.c中对中断向量表进行初始化的函数idt_init。在idt_init函数中,依次对所有中断入口进行初始化。使用mmu.h中的SETGATE宏,填充idt数组内容。每个中断的入口由tools/vectors.c生成,使用trap.c中声明的vectors数组即可

SETGATE宏定义

中断向量表初始定义

idt_init实现

3.请编程完善trap.c中的中断处理函数trap,在对时钟中断进行处理的部分填写trap函数中处理时钟中断的部分,使操作系统每遇到100次时钟中断后,调用print_ticks子程序,向屏幕上打印一行文字”100 ticks”

先在print_ticks函数中写自己想要的输出

然后修改trap_dispatch()函数,让他满足mod100就调用。

然后make qemu运行就可以了

现在回顾一下ucore整个启动过程。
1.CPU初始化CS:IP,启动位于ROM的BIOS(实模式)
2.BIOS通过主引导记录到主引导扇区然后读取bootblock(加载程序)
3.bootblock进入保护模式调用,初始化GDT,调用bootmain
4.bootmain读取磁盘扇区,将系统的elf文件写入运存中,然后找到程序入口,将权限给操作系统。
5.操作系统初始化中断向量表(idt_init函数),然后根据中断向量表开始内核的初始化(kern_init函数)

扩展练习

这篇文章很不错。

https://www.cnblogs.com/whileskies/p/13427877.html

参考bolg:

https://blog.csdn.net/tangyuanzong/article/details/78595854?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166609720716800184180982%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=166609720716800184180982&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-78595854-null-null.142v59pc_rank_34_2,201v3control_2&utm_term=ucore%20lab1&spm=1018.2226.3001.4187

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

  1. 操作系统课程ucore实验 lab1

    Ucore实验lab1 练习一:理解通过make生成执行文件的过程. 在Makefile中生成ucore.img的代码是: $(UCOREIMG): $(kernel) $(bootblock) $( ...

  2. 操作系统 ucore lab1

    操作系统 ucore lab1 实验目的 操作系统是一个软件,也需要通过某种机制加载并运行它.在这里我们将通过另外一个更加简单的软件-bootloader来完成这些工作.为此,我们需要完成一个能够切换 ...

  3. 北航linux内核编译及烧录实验报告,北航操作系统实验Lab1笔记

    Loading... # 北航操作系统实验Lab1 ## Exercise 1.1 - **修改交叉编译路径为 `/OSLAB/compiler/usr/bin/mips_4KC-`** ![ex1_ ...

  4. ucore实验报告lab1

    练习1 1.生成操作系统镜像文件ucore.img 生成ucore.imge的代码如下: $(UCOREIMG): $(kernel) $(bootblock)$(V)dd if=/dev/zero ...

  5. 关于ucore实验一的资料查找

    任务:阅读实验一makefile 搞清楚ucore.img是如何构建的 $@  $<  $^  这三个变量分别是什么意思 https://blog.csdn.net/YEYUANGEN/arti ...

  6. 清华大学ucore实验lab

    清华大学实验lab1 实验目的: 操作系统是一个软件,也需要通过某种机制加载并运行它.在这里我们将通过另外一个更加简单的软件-bootloader来完成这些工作.为此,我们需要完成一个能够切换到x86 ...

  7. 《操作系统》实验报告——熟悉Linux基础命令及进程管理

    理论知识 Linux--进程管理 Linux--Linux C语言编程基础知识 手把手教你安装Linux虚拟机 一.实验目的 (1)加深对进程概念的理解,明确进程和程序的区别. (2)进一步认识并发执 ...

  8. 进程同步算法实现实验报告Linux,操作系统进程同步实验报告.doc

    操作系统进程同步实验报告 实验三:进程同步实验 一.实验任务: (1)掌握操作系统的进程同步原理: (2)熟悉linux的进程同步原语: (3)设计程序,实现经典进程同步问题. 二.实验原理: (1) ...

  9. 计算机操作系统指导书,《计算机操作系统》实验指导书-2015

    q.num++; System.out.println(\已生产第:\个产品!\ try { Thread.currentThread().sleep(100); } catch (Interrupt ...

最新文章

  1. spark 源码分析之七--Spark RPC剖析之RpcEndPoint和RpcEndPointRef剖析
  2. javaScript面向对象表示
  3. jquery-基础事件[下]
  4. Linux-Ubuntu安装JDK
  5. transport方式连接Elasticsearch6.2.3
  6. Navicat 数据库可视化工具远程连接MySQL及基本操作
  7. android 像素密度,屏幕密度,values-sw480dp
  8. 我爱淘冲刺阶段站立会议2每天任务1
  9. android网速代码,Android获取网速和下载速度
  10. Pycharm十大常用快捷键
  11. 根据M3U8文件地址下载网站上的视频
  12. 医咖会SPSS免费教程学习笔记—R*C卡方检验
  13. MTTF、MTBF与MTRF
  14. 打开对方CMD的三种方法
  15. webDav之jackrabbit-webdav基础操作
  16. List------数据结构
  17. rust-hal库嵌入式开发
  18. 【附源码】计算机毕业设计java智能仓储设备管理系统设计与实现
  19. 用nero将rmvb格式影片刻录成vcd光盘
  20. centos7使用dnsmasq搭建dns服务器

热门文章

  1. JavaScript(JS) 音乐播放器
  2. 18-正态映射和位移映射
  3. 中国游戏行业市场分析(一)关于国内游戏制作的问题
  4. 点赞黄文仔董事长,为偏远地区教育助力
  5. python中i表示什么_python中::-1代表什么?
  6. UI UX 小提示合集 -- 第一集
  7. 系统视频播放器——AVPlayerItem AVPlayer AVPlayerLayer
  8. Excel-VBA 快速上手(六、工作表对象、Sheet 页的常用操作)
  9. 简道云教学 | 零代码应用开发软件助力应用型高校学生创新能力培养
  10. Java序列化敏感字段加密