操作系统-ucore-lab1

合集下载

操作系统-ucore-lab3

操作系统-ucore-lab3

操作系统-ucore-lab3操作系统实验报告题⽬:虚拟内存管理⽬录⼀、内容 (2)⼆、⽬的 (3)三、实验流程 (3)四、实验环境与结果分析 (3)五、实验体会和思考题 (10)⼀、内容本次实验是在实验⼆的基础上,借助于页表机制和实验⼀中涉及的中断异常处理机制,完成Page Fault异常处理和FIFO页替换算法的实现,结合磁盘提供的缓存空间,从⽽能够⽀持虚存管理,提供⼀个⽐实际物理内存空间“更⼤”的虚拟内存空间给系统使⽤。

这个实验与实际操作系统中的实现⽐较起来要简单,不过需要了解实验⼀和实验⼆的具体实现。

实际操作系统系统中的虚拟内存管理设计与实现是相当复杂的,涉及到与进程管理系统、⽂件系统等的交叉访问。

如果⼤家有余⼒,可以尝试完成扩展练习,实现extended clock页替换算法。

练习1:给未被映射的地址映射上物理页(需要编程)完成do_pgfault(mm/vmm.c)函数,给未被映射的地址映射上物理页。

设置访问权限的时候需要参考页⾯所在VMA的权限,同时需要注意映射物理页时需要操作内存控制结构所指定的页表,⽽不是内核的页表。

注意:在LAB2EXERCISE1处填写代码。

执⾏make qemu后,如果通过check_pgfault函数的测试后,会有“check_pgfault() succeeded!”的输出,表⽰练习1基本正确。

请在实验报告中简要说明你的设计实现过程。

请回答如下问题:请描述页⽬录项(Pag Director Entry)和页表(Page Table Entry)中组成部分对ucore实现页替换算法的潜在⽤处。

如果ucore的缺页服务例程在执⾏过程中访问内存,出现了页访问异常,请问硬件要做哪些事情?练习2:补充完成基于FIFO的页⾯替换算法(需要编程)完成vmm.c中的do_pgfault函数,并且在实现FIFO算法的swap_fifo.c中完成map_swappable和swap_out_vistim函数。

Ucore-操作系统实验六

Ucore-操作系统实验六

Ucore-操作系统实验六
Ucore-操作系统实验六
1、实验背景
1.1 Ucore操作系统简介
1.2 进程调度算法简介
2、实验目的
2.1 理解进程调度算法的概念和原理
2.2 学习设计和实现进程调度算法
2.3 掌握在Ucore操作系统中实现进程调度算法的方法
3、实验要求
3.1 设计一个合适的进程调度算法
3.2 修改Ucore操作系统的源代码,实现设计的进程调度算法
3.3 运行并测试实现的进程调度算法,确保其功能正确性
4、实验内容
4.1 理解Ucore操作系统的进程管理模块
4.2 设计进程调度算法
4.3 修改进程管理模块的源代码,实现进程调度算法
4.4 编译并运行修改后的Ucore操作系统,进行测试和验证
5、实验步骤
5.1 分析Ucore操作系统的进程管理模块,了解其结构和功能
5.2 设计进程调度算法,考虑算法的可行性和性能
5.3 基于设计的算法,修改进程管理模块的源代码,实现进程调度算法
5.4 编译并运行修改后的Ucore操作系统,进行测试和验证
6、实验结果分析
6.1 比较不同进程调度算法在Ucore操作系统中的性能差异
6.2 分析实验结果,总结优缺点
附件:
- Ucore操作系统源代码:zip
法律名词及注释:
1、版权:指对原创作品(如文学、艺术、音乐、软件等)的独特权利保护。

著作权法是一种保护版权的法律。

2、开源:指软件或硬件的设计和制造过程中,公开的原始设计数据、设计文档、设计过程以及产品源代码等。

3、许可证:是指著作权人授予他人使用其作品或知识产权的权利的行为。

Lab1 介绍

Lab1 介绍

如何进入保护模式

boot/boot.s
lgdt gdtdesc 重新加载gdt表 movl %cr0, %eax orl $CR0_PE_ON, %eax movl %eax, %cr0 设置cr0寄存器,开启保护模式 ljmp $PROT_MODE_CSEG, $protcseg 进入32位模式 .code32 protcseg: # Assemble for 32-bit mode

8个16-bit寄存器( 8个32-bit寄存器的低16位)

8个8-bit寄存器(%ax ~%dx的高8位和低8位)

6个段寄存器

3个控制寄存器

AT&T汇编语法(2)——立即数

使用立即数,要在数前面加符号$

$0x04 \lab1\boot\boot.S
(1)
testb$0x2,%al .set CR0_PE_ON,0x1 orl $CR0_PE_ON, %eax
ELF文件结构
如何在ELF中找到某一段
重要的数据结构 ELF文件头: struct Elf{} 程序头表相对于文件开头的偏 移: Elf->e_phoff 程序头表中段个数: Elf->e_phnum 程序头表中的段: struct Proghdr 段相对于文件开头的偏移: Proghdr->p_offset
AT&T汇编语法(5)——内存寻址

寻址方式

AT&T: displacement(base,index,scale) Intel: [base+index*scale+displacement] -4(%ebp)

清华大学操作系统实验lab1实验报告

清华大学操作系统实验lab1实验报告

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

[练习1.1] 操作系统镜像文件ucore.img 是如何一步一步生成的?在proj1执行命令make V=可以得到make指令执行的过程从这几条指令中可以看出需要生成ucore.img首先需要生成bootblock,而生成bootblock需要先生成bootmain.o和bootasm.o还有sign,这三个文件又分别由bootmain.c、bootasm.S、sigh.c来生成。

ld -m elf_i386 -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o –o obj/bootblock.o这句话用于生成bootblock,elf_i386表示生成elf头,0x7C00为程序的入口。

'obj/bootblock.out' size: 440 bytes这句话表示生成的bootblock的文件大小,因为大小不到512字节,所以需要给blootblock填充,填充的功能在sign.c中有所体现,最后两字节设置为了0x55,0xAAbuf[510] = 0x55;buf[511] = 0xAA;FILE *ofp = fopen(argv[2], "wb+");size = fwrite(buf, 1, 512, ofp);[练习1.2] 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?前面已经提到过:引导扇区的大小为512字节,最后两个字节为标志性结束字节0x55,0xAA,做完这样的检查才能认为是符合规范的磁盘主引导扇区。

Sign.c文件中有作检查:if (size != 512) {fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size);return -1;}练习2:使用qemu执行并调试lab1中的软件。

清华大学操作系统lab1_实验报告

清华大学操作系统lab1_实验报告

实验1:系统软件启动过程练习1:(1)操作系统镜像文件ucore.img 是如何一步一步生成的?在命令行中输入“make V=”1、首先把C的源代码进行编译成为.o文件,也就是目标文件(红色方框内)2、ld命令将这些目标文件转变成可执行文件,比如此处的bootblock.out(绿色方框内)3、dd命令把bootloder放到ucore.img count的虚拟硬盘之中4、还生成了两个软件,一个是Bootloader,另一个是kernel。

(2)一个被系统认为是符合规范的硬盘主引导扇区的特征:在/lab1/tools/sign.c中我们可以了解到规范的硬盘引导扇区的大小为512字节,硬盘结束标志位55AA练习2:(1)从CPU 加电后执行的第一条指令开始,单步跟踪BIOS 的执行改写Makefile文件lab1-mon: $(UCOREIMG)$(V)$(TERMINAL) -e "$(QEMU) -S -s -d in_asm -D $(BINDIR)/q.log -monitor stdio -hda $< -serial null"$(V)sleep 2$(V)$(TERMINAL) -e "gdb -q -x tools/lab1init"在调用qemu时增加-d in_asm -D q.log参数,便可以将运行的汇编指令保存在q.log 中。

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

在tools/gdbinit结尾加上set architecture i8086b *0x7c00 //在0x7c00处设置断点。

continuex /2i $pc //显示当前eip处的汇编指令(3)将执行的汇编代码与bootasm.S 和bootblock.asm 进行比较,看看二者是否一致。

Notice:在q.log中进入BIOS之后的跳转地址与实际应跳转地址不相符,汇编代码也与bootasm.S 和bootblock.asm不相同。

北京大学操作系统实习JOS lab1实验笔记

北京大学操作系统实习JOS lab1实验笔记

OS_lab1最近编辑过的 2011年3月14日系统启动过程:1. 开机以后,计算机将CS=0xF000, IP=0xFFF0, 物理地址为0xFFFF0,这个位置是BIOS ROM所在位置(0xF0000~0xFFFFF),所以机器一开始控制权交给BIOS2. BIOS接到控制权以后,肯定跳转,因为0xFFFF0到0xFFFFF就1Byte信息,这个无法做任何事,所以跳到前面位置开始运行3. BIOS做的工作主要是初始化一些关键的寄存器和中断开关,做完后,BIOS从IDE硬盘第一个扇区512Byte读入boot loader到内存的0x7c00到0x7dff,然后设置CS=0x0000, IP=0x7c00,控制权交给boot loader4. boot loader的任务是将处理器从实模式切换到保护模式,然后将内核kernel从硬盘上读取到内存中,然后切换到内核开始启动操作系统5. kernel放置在硬盘上,在JOS中是存在在了紧接在boot loader的第二个扇区里,如果想修改位置的话,需要修改产生硬盘镜像的kern/Makefrag文件,看到第73行:68 # How to build the kernel disk image69 $(OBJDIR)/kern/kernel.img: $(OBJDIR)/kern/kernel $(OBJDIR)/boot/boot70 @echo + mk $@71 $(V)dd if=/dev/zero of=$(OBJDIR)/kern/kernel.img~ count=10000 2>/dev/null72 $(V)dd if=$(OBJDIR)/boot/boot of=$(OBJDIR)/kern/kernel.img~ conv=notrunc 2>/dev/null73 $(V)dd if=$(OBJDIR)/kern/kernel of=$(OBJDIR)/kern/kernel.img~ seek=1 conv=notrunc 2>/dev/null74 $(V)mv $(OBJDIR)/kern/kernel.img~ $(OBJDIR)/kern/kernel.img75seek = 1表示是硬盘上第二个扇区,72行将boot即是将boot loader放入第一扇区。

ucore操作系统学习(二)ucorelab2物理内存管理分析

ucore操作系统学习(二)ucorelab2物理内存管理分析

ucore操作系统学习(⼆)ucorelab2物理内存管理分析⼀、lab2物理内存管理介绍 操作系统的⼀个主要职责是管理硬件资源,并向应⽤程序提供具有良好抽象的接⼝来使⽤这些资源。

⽽内存作为重要的计算机硬件资源,也必然需要被操作系统统⼀的管理。

最初没有操作系统的情况下,不同的程序通常直接编写物理地址相关的指令。

在多道并发程序的运⾏环境下,这会造成不同程序间由于物理地址的访问冲突,造成数据的相互覆盖,进⽽出错、崩溃。

现代的操作系统在管理内存时,希望达到两个基本⽬标:地址保护和地址独⽴。

地址保护指的是⼀个程序不能随意的访问另⼀个程序的空间,⽽地址独⽴指的是程序指令给出的内存寻址命令是与最终的物理地址⽆关的。

在实现这两个⽬标后,便能够为多道并发程序运⾏时的物理内存访问的隔离提供⽀持。

每个程序在编译、链接后产⽣的最终机器代码都可以使⽤完整的地址空间(虚拟地址),⽽不需要考虑其它的程序的存在。

ucore通过两个连续的实验迭代,lab2和lab3分别实现了物理内存管理和虚拟内存管理(利⽤磁盘缓存⾮⼯作集内存,扩展逻辑上的内存空间)。

ucore的每个实验都是建⽴在前⼀个实验迭代的基础上的,要想更好的理解lab2,最好先理解之前lab1中的内容()。

lab2在lab1平坦模型段机制的基础上,开启了80386的分页机制,并建⽴了内核页表;同时通过硬件中断探测出了当前内存硬件的布局,并以此为依据根据可⽤的内存建⽴了⼀个物理内存管理框架,通过指定某种分配算法,负责处理所有的物理内存页分配与释放的请求。

lab2的代码结构和执⾏流程与lab1差别不⼤,其主要新增了以下功能: 1. bootmain.S中的物理内存探测 2. 在新增的entry.S内核⼊⼝程序中开启了80386页机制 3. kern_init内核总控函数中通过pmm_init函数进⾏整个物理内存管理器的构建初始化⼆、lab2实验细节分析2.1 物理内存布局探测 为了进⾏物理内存的管理,操作系统必须先探测出当前硬件环境下内存的布局,了解具体哪些物理内存空间是可⽤的。

哈工大《操作系统》实验1

哈工大《操作系统》实验1

(5)重新编写一个setup.s,然后将其中的显示的信息改为:“Now we are in SETUP”。

再次编译,重新用make命令生成BootImage,结合提示信息和makefile文修改build.c,具体将setup.s改动如下:mov cx,#27mov bx,#0x0007 ! page 0, attribute 7 (normal)mov bp,#msg1mov ax,#0x1301 ! write string, move cursorint 0x10dieLoop:j dieLoopmsg1:.byte 13,10,13,10.ascii "Now we are in SETUP".byte 13,10,13,10将build.c改动如下:if(strcmp("none",argv[3]) == 0)//添加判断return 0;if ((id=open(argv[3],O_RDONLY,0))<0)die("Unable to open 'system'");// if (read(id,buf,GCC_HEADER) != GCC_HEADER)// die("Unable to read header of 'system'");// if (((long *) buf)[5] != 0)// die("Non-GCC header of 'system'");for (i=0 ; (c=read(id,buf,sizeof buf))>0 ; i+=c )if (write(1,buf,c)!=c)die("Write call failed");close(id);fprintf(stderr,"System is %d bytes.\n",i);if (i > SYS_SIZE*16)die("System is too big");return(0);(6)验证:用make是否能成功生成BootImage,运行run命令验证运行结果。

操作系统实验1-Linux系统操作使用

操作系统实验1-Linux系统操作使用

第1章Linux系统操作使用一、Linux操作系统简介Linux是一种自由和开放源码的类Unix操作系统,存在着许多不同的Linux 版本,但它们都使用了Linux内核。

Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。

Linux是一个领先的操作系统,世界上运算最快的10台超级计算机运行的都是Linux操作系统。

严格来讲,Linux这个词本身只表示Linux内核,但实际上人们已经习惯了用Linux来形容整个基于Linux内核、并且使用GNU 工程各种工具和数据库的操作系统。

Linux得名于天才程序员林纳斯·托瓦兹。

Linux 操作系统的诞生、发展和成长过程始终依赖着五个重要支柱:UNIX 操作系统、MINIX 操作系统、GNU 计划、POSIX 标准和Internet 网络。

1981 年IBM公司推出微型计算机IBM PC。

1991年,GNU计划已经开发出了许多工具软件,最受期盼的GNU C编译器已经出现,GNU的操作系统核心HURD一直处于实验阶段,没有任何可用性,实质上也没能开发出完整的GNU操作系统,但是GNU奠定了Linux用户基础和开发环境。

1991年初,林纳斯·托瓦兹开始在一台386SX兼容微机上学习minix操作系统。

1991年4月,林纳斯·托瓦兹开始酝酿并着手编制自己的操作系统。

1991 年4 月13 日在comp.os.minix 上发布说自己已经成功地将bash 移植到了minix 上,而且已经爱不释手、不能离开这个shell 软件了。

1991年7月3日,第一个与Linux有关的消息是在comp.os.minix上发布的(当然此时还不存在Linux这个名称,当时林纳斯·托瓦兹的脑子里想的可能是FREAX,FREAX的英文含义是怪诞的、怪物、异想天开等)。

1991年的10月5日,林纳斯·托瓦兹在comp.os.minix新闻组上发布消息,正式向外宣布Linux内核的诞生(Freeminix-like kernel sources for 386-AT)。

深入理解计算机系统LAB1实验报告

深入理解计算机系统LAB1实验报告

LAB1实验报告语法检查:正确性检查:1.bitAnd源代码:return ~(~x|~y);思路:可以直接运用摩尔定律,写出与的等价形式。

2.getByte源代码:return (x>>(n<<3))&0xff;思路:向右移动3n位,再用11111111B按位与,截取出所需要的字节3.logicalShift源代码:int logic=~(((1<<31)>>n)<<1);return logic&(x>>n);思路:设置一个变量logic,并通过算数移位将其前n为设置成0,后面32-n位设置为1。

利用这个变量按位与移位后的x即可。

4.bitCount源代码:int bitCount(int x) {int result;int half_one=(0x55)|(0x55<<8);int one=(half_one)|(half_one<<16);int half_two=(0x33)|(0x33<<8);int two=(half_two)|(half_two<<16);int half_three=(0x0f)|(0x0f<<8);int three=(half_three)|(half_three<<16);int four=(0xff)|(0xff<<16);int five=(0xff)|(0xff<<8);result=(x&one)+((x>>1)&one);result=(result&two)+((result>>2)&two);result=(result+(result>>4))&three;result=(result+(result>>8))&four;result=(result+(result>>16))&five;return result;}思路:主要还是使用二分法,通过以为设置五个字符串:010101010101010101010101 0101 01010011 0011 0011 0011 0011 0011 0011 00110000 1111 0000 1111 0000 1111 0000 11110000 0000 1111 1111 0000 0000 1111 11110000 0000 0000 0000 1111 1111 1111 1111分别通过按位与统计1的个数,并将个数记录在下一个字符串1出现的位置。

Lab1.配置存储池和存储空间 windows server 2012

Lab1.配置存储池和存储空间 windows server 2012

实验名称:配置存储池和存储空间实验目标:✧理解如何管理存储与存储空间✧使用‘服务器管理器’和‘控制面板’定义存储空间实验环境:一台安装了WS2012操作系统的物理计算机,以及一台WS2012虚拟机(DC1)。

任务1:创建存储池与存储空间(虚拟磁盘)1.使用管理员帐户Administrator和密码Pa$$w0rd,登录到DC1。

2.在任务栏,单击‘服务器管理器’图标。

3.在‘服务器管理器’窗口,单击‘工具’,在下拉菜单中单击‘计算机管理’。

4.在‘计算机管理’窗口,左侧导航栏中单击‘存储’-‘磁盘管理’,在中间区域,右击‘磁盘1’,在下拉菜单中单击‘联机’。

5.右击‘磁盘1’,在下拉菜单中单击‘初始化磁盘’。

6.在‘初始化磁盘’对话框,选择‘GPT(GUID分区表)’,之后单击‘确定’。

7.按照上述步骤,将其余‘联机’并‘初始化’,之后关闭‘计算机管理’窗口。

8.返回‘服务器管理器’窗口,在左侧导航栏中单击‘文件和存储服务’-‘存储池’,右击‘Primordial’在下拉菜单中单击‘新建存储池…’9.在‘新建存储池向导’的‘指定存储池名称和子系统’页面,‘名称’右侧的文本框中输入存储池名(Pool1),单击‘下一步’。

10.在‘选择存储池的物理磁盘’页面,勾选‘PhysicalDisk1’和‘PhysicalDisk2’,单击‘下一步’。

11.在‘确认选择’页面,单击‘创建’。

关闭。

12.返回‘存储池’窗口,右击之前创建的存储池名(Pool1),在下拉菜单中单击‘新建虚拟磁盘…’。

13.在‘新建虚拟磁盘向导’在‘选择存储池’页面,单击之前创建的存储池(Pool1),单击‘下一步’。

14.在‘指定虚拟磁盘名称’页面,‘名称’右侧的文本框中输入虚拟磁盘名,单击‘下一步’。

15.在‘选择存储数据布局’页面,选择‘Mirrior’,单击‘下一步’。

16.在‘指定设置类型’页面,选择‘精简’,单击‘下一步’。

ucore-LAB1实验报告范文

ucore-LAB1实验报告范文

ucore-LAB1实验报告范文实验目的:操作系统是一个软件,也需要通过某种机制加载并运行它。

在这里我们将通过另外一个更加简单的软件-bootloader来完成这些工作。

为此,我们需要完成一个能够切换到某86的保护模式并显示字符的bootloader,为启动操作系统ucore做准备。

lab1提供了一个非常小的bootloader和ucoreOS,整个bootloader执行代码小于512个字节,这样才能放到硬盘的主引导扇区中。

通过分析和实现这个bootloader和ucoreOS,通过分析和实现这个bootloader和ucoreOS,我们可以了解到:基于分段机制的存储管理设备管理的基本概念PC启动bootloader的过程bootloader的文件组成编译运行bootloader的过程调试bootloader的方法ucoreOS的启动过程在汇编级了解栈的结构和处理过程中断处理机制通过串口/并口/CGA输出字符的方法实验内容:一.练习练习1:理解通过make生成执行文件的过程。

1.ucore.img是如何一步一步生成的?(需要比较详细地解释Makefile中每一条相关命令和命令参数的含义,以及说明命令导致的结果)答:Makefile按照如下步骤生成ucore.img(lab1/bin下的ucore.img):①为每一个源文件(.c和.S文件)产生一个描述其依赖关系的makefile文件,以.d为后缀。

即对于一个源文件“NAME.c”,对应的这个makefile文件为“NAME.d”。

包括分别生成ign.c、bootmain.c、bootam.S的makefile依赖文件ign.d、bootmain.d、bootam.d,具体执行的命令如下:mkdir-pobj/ign/toolgcc-Itool/-g-Wall-O2-MMtool/ign.c-MT\obj/ign/tool/ign.dmkdir-pobj/bootgcc-Iboot/-fno-builtin-Wall-ggdb-m32-notdinc-fno-tack-protector-Ilib/-O-notdinc-MMboot/bootmain.c-MT\gcc-Iboot/-fno-builtin-Wall-ggdb-m32-notdinc-fno-tack-protector-Ilib/-O-notdinc-MMboot/bootam.S-MT\gcc重要的编译参数:-I指定搜索系统头文件的目录,可以重复使用多个该选项指定多个目录-Wall显示所有的警告消息-O2优化(级别为2)-m32指明目标代码32位-O对生成的二进制代码进行尺寸上的优化-ggdb提供编译信息-notdinc只为头文件寻找-I选项指定的目录-fno-builtin除非利用\进行引用,否则不识别所有内建函数-fno-tack-protector不检测缓存溢出②编译源文件,只生成目标文件但不链接。

操作系统实验一

操作系统实验一

操作系统实验一As a person, we must have independent thoughts and personality.本科实验报告操作系统课程名称:学号:姓名:专业:班级:指导教师:课内实验目录及成绩信息技术学院实验(实验一)1 实验名称:基本shell命令及用户管理2 实验目的掌握安装Linux操作系统的方法。

掌握Linux操作系统的基本配置。

了解GNOME桌面环境。

掌握基本shell命令的使用。

3 实验准备下载VMware Workstation虚拟机软件(版本不限)。

准备Linux操作系统的安装源(内核版本和发行版本均不限)。

注:实验准备、实验内容和作为回家作业布置,同学们利用课余时间可在私人计算机上完成。

4 实验要求、步骤及结果安装虚拟机软件。

【操作要求】安装VMware Workstation虚拟机软件,并填写以下4.1.1和的内容。

4.1.1【VMware Workstation虚拟机版本号】4.1.2【主要配置参数】安装Linux操作系统。

【操作要求】安装Linux操作系统,版本不限。

Linux发行版本:Linux内核版本:【主要操作步骤:包括分区情况】1、创建一台虚拟机安装操作系统时客户机操作系统选择Linux2、修改虚拟机的安装路径。

3、建一个新的虚拟磁盘,磁盘的空间20GB,并且将单个文件存储虚拟磁盘。

4、设置分区完毕,安装虚拟机了解Linux操作系统的桌面环境之一GNOME。

【操作要求】查看桌面图标,查看主菜单,查看个人用户主目录等个人使用环境。

【操作步骤1】桌面图标【操作步骤2】主菜单【操作步骤3】个人用户主目录【操作步骤4】启动字符终端【操作步骤5】注销[root@localhost~]# exit【操作步骤6】重启系统[root@localhost~]# reboot【操作步骤7】关闭[root@localhost~]# halt【回答问题】简述Windows桌面环境与Linux桌面环境的主要区别。

ucore文件系统详解

ucore文件系统详解

ucore⽂件系统详解最近⼀直在mooc上学习清华⼤学的操作系统课程,也算是复习下基本概念和原理,为接下来的找⼯作做准备。

每次深⼊底层源码都让我深感操作系统实现的琐碎,即使像ucore这样简单的kernel也让我烦躁不已,⽂件系统相⽐于中断⼦系统、调度⼦系统、进程管理⼦系统等等,要复杂很多,因此被称为⽂件系统⽽不是⽂件⼦系统。

参看⽹络上的资料有时会增加我的困惑,很多⼈只是简单转载,很多细节描述的很模糊,实验环境也各不相同,最终很难深⼊理解⽂件系统的本质,参考源码我觉得有点像从三维世界进⼊到⼆维世界,⼀切变得清晰但是却需要消耗更多精⼒,我觉得这样做是值得的,因为如果不能深⼊⽽只是泛泛的理解,对于操作系统这样偏向于⼯程学的东西来说意义不⼤,本⽂的研究内容主要是根据清华⼤学陈渝副教授、向勇副教授在mooc上所讲,以及实验参考书的内容和我⾃⼰在系统上打的log验证过的,如果有读者发现错误还请批评指正。

本篇博客希望尽可能照顾到初学者,但是有些简单原理默认读者已经掌握,很多细节不会展开叙述,读者请⾃⾏Google或者参看Intel Development Manual,实验⽤的源码来⾃于清华⼤学教学操作系统,读者可在github上搜索ucore_os_lab。

附上ucore的实验参考书.综述最初实现⽂件系统是为了实现对磁盘数据的⾼效管理,使得⽤户可以⾼效、快速的读取磁盘上的数据内容。

其实我个⼈觉得⽂件系统就是操作系统内核和外设之间的⼀套输⼊输出协议,我们所采⽤的算法和索引结点的建⽴⽅式都是为了根据实际应⽤情况所设计的⼀套⾼效的协议。

在实现的过程中,我们还要将⽂件系统进⾏分层抽象,也就是实现我们的虚拟⽂件系统,虚拟⽂件系统有对上和对下两个⽅⾯,对上是为了向上层应⽤提供⼀套通⽤的访问接⼝,可以⽤相同的⽅式去访问磁盘上的⽂件(包括⽬录,后⾯会介绍)和外设;对下兼容不同的⽂件系统和外设。

虚拟⽂件系统的层次和依赖关系如下图所⽰:对照上⾯的层次我们再⼤致介绍⼀下⽂件系统的访问处理过程,加深对⽂件系统的总体理解。

清华大学操作系统ucore实验2物理内存管理

清华大学操作系统ucore实验2物理内存管理

Lab2实验报告一、练习0:填写已有实验利用Understand中的Compare完成此练习。

二、练习1:实现firstfit连续物理内存分配算法大体思路:物理内存页管理器顺着双向链表进行搜索空闲内存区域,直到找到一个足够大的空闲区域,这是一种速度很快的算法,因为它尽可能少地搜索链表。

如果空闲区域的大小和申请分配的大小正好一样,则把这个空闲区域分配出去,成功返回;否则将该空闲区分为两部分,一部分区域与申请分配的大小相等,把它分配出去,剩下的一部分区域形成新的空闲区。

其释放内存的设计思路很简单,只需把这块区域重新放回双向链表中即可。

实现目标:重写default_init_memmap(),default_alloc_pages(),default_free_pages()函数。

具体细节:a)default_init_memmap()函数这个函数是用来初始化空闲页链表的。

主要有两个步骤:初始化每一个空闲页,计算空闲页的总数。

注意:1.使用头插法是因为地址是从低地址向高地址增长。

2.p->flags = 0语句已经将PG_reserved标志位置零。

b)default_alloc_pages()函数这个函数是用来分配空闲页的。

主要步骤如下:1.寻找足够大的空闲块1.1.如果找到了,重新设置标志位1.2.从空闲链表中删除此页1.3.判断空闲块大小是否合适1.3.1.如果不合适,分割页块1.3.2.如果合适则不进行操作1.4.计算剩余空闲页个数1.5.返回分配的页块地址备注:在参考答案中,我认为有些语句是冗余的,如图:验证:在第二次重置标志位前后,分别输出标志位的值,发现,flags 并没有发生变化。

然后将这两句话注释,编译后运行,依旧可以得到正确答案。

所以我认为这两句话是没有必要的。

c)default_free_pages()函数这个函数的作用是释放已经使用完的页,把他们合并到freelist中。

lab1练习6中断向量表的初始化

lab1练习6中断向量表的初始化

lab1练习6中断向量表的初始化练习6:完善中断初始化和处理(需要编程)请完成编码⼯作和回答如下问题:1. 中断描述符表(也可简称为保护模式下的中断向量表)中⼀个表项占多少字节?其中哪⼏位代表中断处理代码的⼊⼝?2. 请编程完善kern/trap/trap.c中对中断向量表进⾏初始化的函数idt_init。

在idt_init函数中,依次对所有中断⼊⼝进⾏初始化。

使⽤mmu.h中的SETGATE宏,填充idt数组内容。

每个中断的⼊⼝由tools/vectors.c⽣成,使⽤trap.c中声明的vectors数组即可。

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

【注意】除了系统调⽤中断(T_SYSCALL)使⽤陷阱门描述符且权限为⽤户态权限以外,其它中断均使⽤特权级(DPL)为0的中断门描述符,权限为内核态权限;⽽ucore的应⽤程序处于特权级3,需要采⽤`int 0x80`指令操作(这种⽅式称为软中断,软件中断,Tra中断,在lab5会碰到)来发出系统调⽤请求,并要能实现从特权级3到特权级0的转换,所以系统调⽤中断(T_SYSCALL)所对应的中断门描述符中的特权级(DPL)需要设置为3。

要求完成问题2和问题3 提出的相关函数实现,提交改进后的源代码包(可以编译执⾏),并在实验报告中简要说明实现过程,并写出对问题1的回答。

完成这问题2和3要求的部分代码后,运⾏整个系统,可以看到⼤约每1秒会输出⼀次”100 ticks”,⽽按下的键也会在屏幕上显⽰。

提⽰:可阅读⼩节“中断与异常”。

⼩节“中断与异常”:实验报告:练习61.中断描述符表(也可简称为保护模式下的中断向量表)中⼀个表项占多少字节?其中哪⼏位代表中断处理代码的⼊⼝?中断描述符表⼀个表项占8个字节。

操作系统lab1 (12)

操作系统lab1 (12)

Linux操作实验12
实验目的:
1.学习Bourne shell 是如何处理数字数据的
2.学习Bourne shell 中的数组
3.学习Bourne shell 如何使用信号/中断处理
4.学习如何使用文件描述符来进行文件I/O操作
5.学习Bourne shell 脚本如何使用函数
6.学习如何进行Bourne shell 脚本的调试
实验内容:
1.进入你的linux系统
2.在目录中创建教材中addall脚本并运行,用Fiboracci数列的前10个数做
参数,结果是什么?程序生成了正确的结果了吗?
3.写一个Bourne shell脚本,包含两个数字数组array1和array2,分别初始
化为{1,2,3,4,5}和{1,4,9,16,25}。

脚本生成并显示一个数组,其中的元素是这两个数组中对应元素的和,数组中第一个元素是1+1=2,第2个元素是2+4=6等。

4.写出一个命令将shell的stdin更改到当前目录下名为data文件,stdout更
改到当前目录下名为out的文件。

如果data文件包含下面的内容,那么在命令执行后会发生什么?
echo –n “The time now is:”
date
echo –n “The users presently logged on are:”
who
5.退出系统.。

操作系统实验指导书

操作系统实验指导书

《操作系统》课程实验指导书信电工程学院2011年9月目录前言 (1)实验要求 (2)实验准备 (3)实验一处理机管理 (4)实验二存储管理 (7)实验三设备管理 (10)实验四文件管理 (14)前言“操作系统”是计算机及相关专业的必修课程,在学习计算机操作系统理论的同时,通过实验可以加强对操作系统基本原理的理解。

让学生通过上机实验验证计算机操作系统的难点,增加学生对计算机操作系统的领悟和掌握。

使学生对计算机操作系统的工作原理和工作过程有深刻的体会和理解,同时又锻炼了程序编制能力和学生创造能力。

本课程共设8个学时,实验主要由进程管理、存储管理、设备管理、文件管理等4个主要几个部分所组成。

其中验证类实验占25%、设计类实验占75%,每个实验2学时。

考虑由于学生C语言基础较并且不平衡,本课程实验安排了实验准备(由学生课下完成),主要了解掌握TurboC2.0编程环境、掌握C语言编程的基本编制方法和技巧,为后继的实验做准备。

这些实验能很好地解决配合操作系统课程教学来指导学生进行实践的问题。

实验要求1.学生按照实验要求,上机前写好上机实验预习报告,内容包括:实验的目的、内容、实验步骤(程序)。

2.上机实验时按实验要求完成每一个实验的内容。

3.课后认真书写实验报告。

实验报告采用统一的实验报告纸,实验封面包括:课程名称、实验名称、实验序号、班级、姓名、学号、实验时间。

实验报告书写规范,应包括:实验目的和要求、实验内容、实验步骤、实验记录(程序)。

4.遵守机房纪律,服从辅导员教师指挥,爱护实验设备。

5.实验课程不迟到。

如有事不能出席,所缺实验一般不补。

实验准备一.实验目的熟悉TurboC2.0基本编程环境掌握C语言的基本编程方法二.实验内容与要求(一)TurboC2.0的基本操作1、TurboC2.0的基本操作2、运行一个C语言程序的一般过程3、编辑并保存存一个C语言程序4、编译、链接源程序文件5、运行与查看程序结果(二)C语言程序的基本编程方法1、数据类型、运算符、表达式2、数据的输入、输出3、C语言程序的基本控制结构4、数组5、函数与程序结构6、指针7、结构与联合8、文件操作(可通过网络查找TurboC2.0用户说明书,并在其指导进行操作,并要求人手一本C语言程序设计教材并上机练习)实验一处理机管理一、掌握进程及进程调度的概念、三种基本状态及转换二、实验内容1、复习进程的概念、进程调度的含义、进程的三种基本状态及转换2、编制一个模拟进程调度的程序三、参考程序#include"stdio.h"#define running 1/*用running表示进程处于运行状态*/#define aready 2/*用aready表示进程处于就绪状态*/#define blocking 3/*用blocking表示进程处于等待状态*/#define sometimes 5/*用sometime表示时间片大小*/#define n 10/*假定系统允许进程个数为10*/struct{int name;/*进程标识符*/int status;/*进程状态*/int ax,bx,cx,dx;/*进程现场信息,通用寄存器内容*/int pc;/*进程现场信息,程序计数器内容*/int psw;/*进程现场信息,程序状态寄存器内容*/int next;/*下一个进程控制块的位置*/}pcbarea[n];/*定义模拟进程控制块区域的数组*/int PSW,AX,BX,CX,DX,PC,TIME;/*模拟寄存器*/int run;/*定义指向正在运行进程的进程控制块的指针*/struct{int head;int tail;}ready;/*定义指向正在运行进程的进程控制块的指针*/int block;/*定义指向等待队列的指针*/int pfree;/*定义指向空闲进程控制块队列的指针*/sheduling()/*进程调度函数*/{int i;if(ready.head==-1)/*空闲进程控制块队列的指针*/{printf("无就绪进程\n");return 0;}i=ready.head;/*就绪队列头指针赋给i*/ready.head=pcbarea[ready.head].next;/*就绪队列头指针后移*/if(ready.head==-1) ready.tail=-1;/*就绪队列为空,修正尾指针 ready.tail*/pcbarea[i].status=running;/**/TIME=sometimes;/*设置相对时钟寄存器*//*恢复该进程现场信息*/AX=pcbarea[run].ax;BX=pcbarea[run].bx;CX=pcbarea[run].cx;DX=pcbarea[run].dx;PC=pcbarea[run].pc;PSW=pcbarea[run].psw;/*修改指向运行进程的指针*/run=i;return 0;}/*进程调度函数结束*/create(int x)/*创建进程*/{int i;if(pfree==-1)/*空闲进程控制块队列为空*/{printf("无空闲进程控制块,进程创建失败\n");return 0;}i=pfree;/*取空闲进程控制块队列的第一个*/pfree=pcbarea[pfree].next;/*pfree后移*//*填写该进程控制内容:*/pcbarea[i].name=x;pcbarea[i].status=aready;pcbarea[i].ax=x;pcbarea[i].bx=x;pcbarea[i].cx=x;pcbarea[i].dx=x;pcbarea[i].pc=x;pcbarea[i].psw=x;if(ready.head!=-1){/*就绪队列不空时,挂入就绪队列方式*/pcbarea[ready.tail].next=i;ready.tail=i;pcbarea[ready.tail].next=-1;}else{/*就绪队列为空时,挂入就绪队列方式*/ready.head=i;ready.tail=i;pcbarea[ready.tail].next=-1;}return 0;}/*进程创建函数结束*/main(){/*系统初始化*/int num,j;run=ready.head=ready.tail=block=-1;pfree=0;for(j=0;j<n-1;j++)pcbarea[j].next=j+1;pcbarea[n-1].next=-1;printf("输入进程编号(避免编号的冲突,以负数输入结束,最多可以创建10个进程):\n");scanf("%d",&num);while(num>0){create(num);scanf("%d",&num);}sheduling();if(num!=-1){printf("进程名进程状态寄存器内容:ax bx cx dx pc psw:\n");printf("%4d%10d%3d%3d%3d%3d%3d%3d\n",pcbarea[run].name,pcbarea[run].status,pcbarea[ run].ax,pcbarea[run].bx,pcbarea[run].cx,pcbarea[run].dx,pcbarea[run].pc,pcbarea[run ].psw);}}/*main结束*/实验二存储管理一、实验目的掌握分页存储管理的基本原理及分页存储管理中的地址变换过程二、实验内容1、复习分页想念管理的基本概念、基本原理、及地址变换过程2、编制一个模拟地址变换过程的程序三、参考程序/*页式虚拟存储管理中地址转换和缺页中断的模拟*/#include"stdio.h"#define n 64/*模拟实验中假定的页表长度*/#define length 10struct{int lnumber;/*页号*/int flag;/*表示该页是否在主存,"1"表示在主存,"0"表示不在*/int pnumber;/*该页所在主存块的块号*/int write;/*该页号是否被修改,"1"表示修改过,"0"表示末修改过*/ int dnumber;/*该页存放在磁盘上的位置,即磁盘块号*/}page[n];/*页表定义*/int m;/*m为该作业在主存中的主存块块数*/int page_length;/*页表实际长度*/int p[length];/*存放在主存中页的页号*/int head;/*主存中页号队列*/page_interrupt(lnumber)int lnumber;{int j;printf("发生缺页中断*%d\n",lnumber);/*淘汰页*/j=p[head];p[head]=lnumber;head=(head+1)%m;if(page[j].write==1)printf("将页%d写回磁盘第%d块\n",j,page[j].dnumber);page[j].flag=0;/*第j页存在标志改为"0"*/page[lnumber].pnumber=page[j].pnumber;page[lnumber].flag=1;/*第lnumber页存在标志改为"0"*/page[lnumber].write=0;/*第lnumber页修改标志改为"1"*/printf("淘汰主存块%2d中的页%2d从磁盘第%d块中调入页%2d\n",page[j].pnumber,j,page[lnumber].dnumber,lnumber);}/*缺页中断处理函数结束*/void command(laddress,write)unsigned laddress;int write;{int paddress,ad,pnumber,lnumber;kk:/*取出逻辑地址laddress的页号lnumber(高6位)和页内地址ad*/ lnumber=laddress>>10;ad=laddress&0x3ff;if(lnumber>=page_length){printf("不存在该页\n");}if(page[lnumber].flag==1)/*页在主存*/{pnumber=page[lnumber].pnumber;/*从页表中取得块号*/paddress=pnumber<<10|ad;/*合并块号和块内地址形成物理地址padress*/printf("逻辑地址是:%x 对应的物理地址是%x\n:",laddress,paddress);}if(write==1)/*如果需要写,修改页的修改标志位*/page[lnumber].write=1;else{page_interrupt(lnumber);/*缺页中断*/goto kk;}}/*命令处理函数结束*/void main(){int lnumber,pnumber,write,dnumber;unsigned laddress;int i;/*输入页表信息,页号从0开始,依次编号,创建页表page*/printf("输入页表信息,创建页表(若页号为-1,则结束输入\n");printf("输入页号和辅存地址");scanf("%d%d",&lnumber,&dnumber);i=0;while(lnumber!=-1){page[i].lnumber=lnumber;page[i].flag=0;page[i].write=0;page[i].dnumber=dnumber;i++;printf("输入页号和辅存地址");scanf("%d%d",&lnumber,&dnumber);}page_length=i;printf("输入主存号,主存块数要小于%d,(以-1结束):",i);scanf("%d",&pnumber);m=0;head=0;while(pnumber!=-1){if(m<=i){page[m].pnumber=pnumber;page[m].flag=1;p[m]=m;m++;}scanf("%d",&pnumber);}printf("输入指令性质(1-修改,0-不需要,其他--结束程序运行)和逻辑地址:");scanf("%d%x",&write,&laddress);while(write==0||write==1){command(laddress,write);/**/printf("输入指令性质(1-修改,0-不需要,其他--结束程序运行)和逻辑地址:");scanf("%d%x",&write,&laddress);}}/*函数结束*/实验三设备管理一、实验目的了解设备管理的基本原理、设备的分配与回收过程二、实验内容1、复习设备管理的基本概念、基本原理、常用的数据结构、分配策略及算法2、编制一个独占设备的分配和回收模拟程序三、参考程序/*独占设备的分配和回收模拟*/#include"stdio.h"#include"string.h"#define false 0#define true 1#define n 4#define m 10struct{char type[10];/*设备类名*/int count ;/*拥有设备台数*/int remain;/*现存的可用设备台数*/int address;/*该类设备在设备表中的起始地址*/}equiptype[n];/*设备类表定义,假定系统有N个设备类型*/struct{int number;/*设备绝对号*/int status;/*设备好坏状态*/int remain;/*设备是否已分配*/char jobname[4];/*占有设备的作业名*/int lnumber;/*设备相对号*/}equipment[m];/*设备表定义,假定系统有M个设备*/allocate(char J[],char type[],int mm){int i,t;/*查询该类设备*/i=0;while(i<n&&strcmp(equiptype[i].type,type)!=0)i++;if(i>=n)/*没有找到该类设备*/{printf("无该类设备,设备分配失败");return(false);}if(equiptype[i].remain<1)/*所需设备现存可用台数不足*/{printf("该类设备不足,分配失败");return(false);}t=equiptype[i].address;/*取出该类设备在设备表中的起始地址*/while(!(equipment[t].status==1&&equipment[t].remain==0))t++;/*填写作业名、相对号,状态改为已分配*/equiptype[i].remain--;equipment[t].remain=1;strcpy(equipment[t].jobname,J);equipment[t].lnumber=mm;}/*设备分配函数结束*/reclain(char J[],char type[]){int i,t,j,k,nn;i=0;while(i<n&&strcmp(equiptype[i].type,type)!=0)i++;if(i>=n)/*没有找天该类设备*/{printf("无该类设备,设备回收失败");return(false);}t=equiptype[i].address;/*取出该类设备在设备表中的起始地址*/ j=equiptype[i].count;/*取出该类设备的数量*/k=0;nn=t+j;for(;t<nn;t++)if(strcmp(equipment[t].jobname,J)==0&&equipment[t].remain==1) {equipment[t].remain=0;k++;}equiptype[i].remain=equiptype[i].remain+k;if(k==0)printf("该作业没有使用该类设备\n");}/*设备收回函数结束*/main(){char J[4];int i,mm,a;char type[10];/*设备类表初始化:*/strcpy(equiptype[0].type,"input");/*输入机*/equiptype[0].count=2;equiptype[0].remain=2;equiptype[0].address=0;strcpy(equiptype[1].type,"printer");/*打印机*/equiptype[1].count=3;equiptype[1].remain=3;equiptype[1].address=2;strcpy(equiptype[2].type,"disk");/*磁盘机*/equiptype[2].count=4;equiptype[2].remain=4;equiptype[2].address=5;strcpy(equiptype[3].type,"tape");/*磁带机*/equiptype[3].count=1;equiptype[3].remain=1;equiptype[3].address=9;/*设备表初始化:*/for(i=0;i<10;i++){equipment[i].number=i;equipment[i].status=1; //343434equipment[i].remain=0;}while(1){printf("\n0-退出,1-分配,2-回收,3-显示");printf("\n选择功能项(0-3):");scanf("%d",&a);switch(a){case 0:/*程序结束*/return(false) ;case 1:/*a=1分配设备*/printf("输入作业名、作业所需设备类和设备相对号");scanf("%s%s%d",J,type,&mm);allocate(J,type,mm);/*分配设备*/break;case 2:/*a=2回收设备*/printf("输入作业名和作业归还的设备类");scanf("%s%s",J,type);reclain(J,type);/*回收设备*/break;case 3:/*a=3输出设备类表和设备表的内容*/printf("\n输出设备类表\n");printf(" 设备类型设备总量空闲好设备\n");for(i=0;i<n;i++)printf("%9s%8d%9d\n",equiptype[i].type,equiptype[i].count,equiptype[i].remain); printf("输出设备表:\n");printf("绝对号好/坏已/未分配占用作业名相对号\n");for(i=9;i<m;i++)printf("%3d%8d%9d%12s%8d\n",equipment[i].number,equipment[i].status,equipment[i].remain,equipment[i].jobname,equipment[i].lnumber);}}}。

Ucore-操作系统实验六

Ucore-操作系统实验六

调度器一、实验目的理解操作系统的调度管理机制熟悉ucore 的系统调度器框架,以及缺省的Round-Robin 调度算法基于调度器框架实现一个(Stride Scheduli ng调度算法来替换缺省的调度算法二、实验内容实验五完成了用户进程的管理,可在用户态运行多个进程。

但到目前为止,采用的调度策略是很简单的FIFO调度策略。

本次实验,主要是熟悉ucore的系统调度器框架,以及基于此框架的Rou nd-Robi n( RR)调度算法。

然后参考RR 调度算法的实现,完成Stride Scheduli ngj调度算法。

三、实验要求1) 基于markdown 格式来完成,以文本方式为主2) 填写各个基本练习中要求完成的报告内容3) 完成实验后,请分析ucore_lab中提供的参考答案4) 并请在实验报告中说明你的实现与参考答案的区别5) 列出你认为本实验中重要的知识点,以及与对应的OS 原理中的知识6) 简要说明你对二者的含义,关系差异等方面的理解。

( 也可能出现实验中的知识点没有对应的原理知识点)7) 列出你认为OS 原理中很重要,但在实验中没有对应上的知识四、实验原理调度本质上体现了对CPU 资源的抢占。

对于用户进程而言,由于有中断的产生,可以随时打断用户进程的执行,转到操作系统内部,从而给了操作系统以调度控制权,让操作系统可以根据具体情况(比如用户进程时间片已经用完了)选择其他用户进程执行。

这体现了用户进程的可抢占性(preemptive。

但如果把ucore操作系统也看成是一个特殊的内核进程或多个内核线程的集合,那ucore是否也是可抢占的呢?其实ucore内核执行是不可抢占的(non-preemptive,即在执行“任意”内核代码时,CPU 控制权可被强制剥夺五、实验流程1)在实验五,创建了用户进程,并让它们正确运行。

这中间也实现了FIFO 调度策略。

可通过阅读实验五下的kern/schedule/sched.c 的schedule 函数的实现来了解其FIFO 调度策略。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

HUNAN UNIVERSITY操作系统实验报告目录一、内容 (2)二、目的 (2)三、实验设计思想和流程 (3)四、主要文件结构说明 (4)五、实验环境以及实验过程与结果分析(包含实验详细过程) (4)练习1:理解通过make生成执行文件的过程 (4)练习2:使用qemu执行并调试lab1中的软件。

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

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

(11)练习5:实现函数调用堆栈跟踪函数 (14)练习6:完善中断初始化和处理 (16)六、实验体会 (18)一、内容lab1中包含一个bootloader和一个OS。

这个bootloader可以切换到X86保护模式,能够读磁盘并加载ELF执行文件格式,并显示字符。

而这lab1中的OS只是一个可以处理时钟中断和显示字符的幼儿园级别OS。

为了实现lab1的目标,lab1提供了6个基本练习和1个扩展练习,要求完成实验报告。

二、目的操作系统是一个软件,也需要通过某种机制加载并运行它。

在这里我们将通过另外一个更加简单的软件-bootloader来完成这些工作。

为此,我们需要完成一个能够切换到x86的保护模式并显示字符的bootloader,为启动操作系统ucore做准备。

lab1 提供了一个非常小的bootloader和ucore OS,整个bootloader执行代码小于512个字节,这样才能放到硬盘的主引导扇区中。

通过分析和实现这个bootloader和ucore OS,读者可以了解到:计算机原理CPU的编址与寻址: 基于分段机制的内存管理CPU的中断机制外设:串口/并口/CGA,时钟,硬盘Bootloader软件编译运行bootloader的过程调试bootloader的方法PC启动bootloader的过程ELF执行文件的格式和加载外设访问:读硬盘,在CGA上显示字符串ucore OS软件编译运行ucore OS的过程ucore OS的启动过程调试ucore OS的方法函数调用关系:在汇编级了解函数调用栈的结构和处理过程中断管理:与软件相关的中断处理外设管理:时钟三、实验设计思想和流程依照实验指导书完成了六个对应练习:练习1:理解通过make生成执行文件的过程练习2:使用qemu执行并调试lab1中的软件练习3:分析bootloader进入保护模式的过程练习4:分析bootloader加载ELF格式的OS的过程练习5:实现函数调用堆栈跟踪函数练习6:完善中断初始化和处理四、主要文件结构说明五、实验环境以及实验过程与结果分析(包含实验详细过程)实验环境为:LINUX_64系统。

练习1:理解通过make生成执行文件的过程1.操作系统镜像文件ucore.img是如何一步一步生成的?(需要比较详细地解释Makefile中每一条相关命令和命令参数的含义,以及说明命令导致的结果)# create ucore.imgUCOREIMG := $(call totarget,ucore.img)$(UCOREIMG): $(kernel) $(bootblock)$(V)dd if=/dev/zero of=$@ count=10000$(V)dd if=$(bootblock) of=$@ conv=notrunc$(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc$(call create_target,ucore.img)//为了生成bootblock,首先需要生成bootasm.o、bootmain.o、sign$(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign) @echo + ld $@$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock) @$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock)@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock) @$(call totarget,sign) $(call outfile,bootblock) $(bootblock)$(call create_target,bootblock)(1)通过GCC编译器将Kernel目录下的.c文件编译成OBJ目录下的.o文件。

(2)ld命令根据链接脚本文件kernel.ld将生成的*.o文件,链接成BIN目录下的kernel文件(3)通过GCC编译器将boot目录下的.c, .S文件以及tools目录下的sign.c文件编译成OBJ 目录下的*.o文件。

(4)ld命令将生成的*.o文件,链接成BIN目录下的bootblock文件。

(5)dd命令将dev/zero, bin/bootblock,bin/kernel 写入到bin/ucore.img注:/dev/zero文件代表一个永远输出 0的设备文件,使用它作输入可以得到全为空的文件。

因此可用来创建新文件和以覆盖的方式清除旧文件。

下面使用dd命令将从zero设备中创建一个10K大小(bs决定每次读写1024字节,count定义读写次数为10次),但内容全为0的文件。

dd 是 Linux/UNIX 下的一个非常有用的命令,作用是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。

2.一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?从以下代码可以看出:buf缓冲区最后两位为0x55和0xAA,并且需要扇区大小满足512字节。

(1) 编译过程:在解压缩后的ucore 源码包中使用make 命令即可。

例如lab1中:chy@laptop: ~/lab1$ make在lab1目录下的bin目录中,生成一系列的目标文件:ucore.img:被qemu访问的虚拟硬盘文件kernel: ELF格式的toy ucore kernel执行文,被嵌入到了ucore.img中bootblock: 虚拟的硬盘主引导扇区(512字节),包含了bootloader执行代码,被嵌入到了ucore.img中sign:外部执行程序,用来生成虚拟的硬盘主引导扇区还生成了其他很多文件,这里就不一一列举了。

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

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

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

2.3. 从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和bootblock.asm进行比较。

(1)修改Makefile中debug段代码,使其默认执行如下命令:-S -s$(V)$(QEMU) -e “$(QEMU) –S –s –d in_asm –D $(BINDIR)/q.log -parallel stdio -hda $< -serial null “为了与qemu配合进行源代码级别的调试,需要先让qemu进入等待gdb调试器的接入并且还不能让qemu中的CPU执行,因此启动qemu的时候,我们需要使用参数-S、–s这两个参数来做到这一点。

修改gdbinit文件如下:执行make debug(2)q.log(3)q.log与bootasm.S和bootblock.asm中的代码相同。

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

修改debuginit如下:断点设置正常:练习3:分析bootloader进入保护模式的过程。

BIOS将通过读取硬盘主引导扇区到内存,并转跳到对应内存中的位置执行bootloader。

请分析bootloader是如何完成从实模式进入保护模式的。

//关中断和清除段数据:包括将flag置0和将段寄存器置0.globl startstart:.code16cli //关中断cld //清除方向标志xorw %ax, %ax //ax清0movw %ax, %ds //ds清0movw %ax, %es //es清0movw %ax, %ss //ss清0开启A20:通过将键盘控制器上的A20线置于高电位,全部32条地址线可用,可以访问4G的内存空间。

seta20.1: # 等待8042键盘控制器不忙inb $0x64, %al #testb $0x2, %al #jnz seta20.1 #movb $0xd1, %al # 发送写8042输出端口的指令outb %al, $0x64 #seta20.1: # 等待8042键盘控制器不忙inb $0x64, %al #testb $0x2, %al #jnz seta20.1 #movb $0xdf, %al # 打开A20outb %al, $0x60 #初始化GDT表:一个简单的GDT表和其描述符已经静态储存在引导区中,载入即可lgdt gdtdesc进入保护模式:通过将cr0寄存器PE位置1便开启了保护模式movl %cr0, %eaxorl $CR0_PE_ON, %eaxmovl %eax, %cr0通过长跳转更新cs的基地址ljmp $PROT_MODE_CSEG, $protcseg.code32protcseg:设置段寄存器,并建立堆栈movw $PROT_MODE_DSEG, %axmovw %ax, %dsmovw %ax, %esmovw %ax, %fsmovw %ax, %gsmovw %ax, %ssmovl $0x0, %ebpmovl $start, %esp转到保护模式完成,进入boot主方法call bootmain练习4:分析bootloader加载ELF格式的OS的过程。

通过阅读bootmain.c,了解bootloader如何加载ELF文件。

通过分析源代码和通过qemu来运行并调试bootloader&OSbootloader如何读取硬盘扇区的?bootloader是如何加载ELF格式的OS?要了解bootloader是如何读取硬盘扇区的需要了解elf.h文件读取扇区static voidreadsect(void *dst, uint32_t secno) {waitdisk();outb(0x1F2, 1); // 设置读取扇区的数目为1outb(0x1F3, secno & 0xFF);outb(0x1F4, (secno >> 8) & 0xFF);outb(0x1F5, (secno >> 16) & 0xFF);outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);// 上面四条指令联合制定了扇区号// 在这4个字节线联合构成的32位参数中// 29-31位强制设为1// 28位(=0)表示访问"Disk 0"// 0-27位是28位的偏移量outb(0x1F7, 0x20); // 0x20命令,读取扇区waitdisk();insl(0x1F0, dst, SECTSIZE / 4); // 读取到dst位置,// 幻数4因为这里以DW为单位}/* file header */struct elfhdr {uint32_t e_magic; // must equal ELF_MAGIC elf的模数uint8_t e_elf[12];uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core imageuint16_t e_machine; // 3=x86, 4=68K, etc.uint32_t e_version; // file version, always 1uint32_t e_entry; // entry point if executable 入口地址uint32_t e_phoff; // file position of program header or 0第一个programheader的位置,//这是个结构体,通过这个指针可以找到结构体数组的位置结合e_phnum可以取得所有ph结构体uint32_t e_shoff; // file position of section header or 0uint32_t e_flags; // architecture-specific flags, usually 0uint16_t e_ehsize; // size of this elf headeruint16_t e_phentsize; // size of an entry in program headeruint16_t e_phnum; // number of entries in program header or 0uint16_t e_shentsize; // size of an entry in section headeruint16_t e_shnum; // number of entries in section header or 0uint16_t e_shstrndx; // section number that contains section name strings};/* program section header */struct proghdr {uint32_t p_type; // loadable code or data, dynamic linking info,etc.uint32_t p_offset; // file offset of segmentuint32_t p_va; // virtual address to map segmentuint32_t p_pa; // physical address, not useduint32_t p_filesz; // size of segment in fileuint32_t p_memsz; // size of segment in memory (bigger if contains bss)uint32_t p_flags; // read/write/execute bitsuint32_t p_align; // required alignment, invariably hardware page size };bootloader如何加载ELF格式的OS:在bootmain函数中,voidbootmain(void) {// 首先读取ELF的头部readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0);// 通过储存在头部的幻数判断是否是合法的ELF文件if (ELFHDR->e_magic != ELF_MAGIC) {goto bad;}struct proghdr *ph, *eph;// ELF头部有描述ELF文件应加载到内存什么位置的描述表,// 先将描述表的头地址存在phph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff);eph = ph + ELFHDR->e_phnum;// 按照描述表将ELF文件中数据载入内存for (; ph < eph; ph ++) {readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);}// ELF文件0x1000位置后面的0xd1ec比特被载入内存0x00100000// ELF文件0xf000位置后面的0x1d20比特被载入内存0x0010e000// 根据ELF头部储存的入口信息,找到内核的入口((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();bad:outw(0x8A00, 0x8A00);outw(0x8A00, 0x8E00);while (1);}练习5:实现函数调用堆栈跟踪函数我们需要在lab1中完成kdebug.c中函数print_stackframe的实现,可以通过函数print_stackframe来跟踪函数调用堆栈中记录的返回地址。

相关文档
最新文档