毕业论文终稿(一个简易操作系统的实现)

更新时间:2024-06-27 09:05:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

摘 要

作为学习操作系统的新手,商用的操作系统设计复杂,代码量大,不适合作为学习的蓝本,如果能够从零开始开发一个小型的操作系统雏形,不但可以全面深入地了解操作系统的工作原理,还可以为读大型开源操作系统的代码积累经验。

这个取名为MyOS的从头开始开发的系统虽然结构简单,但是具备了内存管理、多任务控制、任务切换、基本输入输出管理并且运行在32位保护模式下,在用户接口方面,它采用的是图形界面和命令行界面混合的方式来与用户交互,在程序接口方面,设计了部分API,主要是针对于图形显示和计时器两方面,本文利用这些API,实现了“贪吃蛇”、“时钟计时器”两个例子程序。

本文首先介绍操作系统运行所依赖的硬件系统组成,然后重点地介绍了操作系统的基本特征和主要功能,接着详细地说明了80386以上Intel CPU所运行的贯穿整个操作系统开发的32位保护模式,接下来简述了MyOS的开发环境和语言,以及MyOS的基本运行原理,然后重点介绍了MyOS的设计与实现,最后叙述MyOS的程序接口API,以及利用MyOS中的API设计的应用程序。

关键词:操作系统,运行原理,32保护模式

I

ABSTRACT

As a fresh man who want to learn Operating System deeply, commercial using OS are too complex to learn .So ,if we develop a demo of OS from blank, if we start from scratch ,we can not only have a good know for the running principle of a OS, but also accumulate many experience which are of significant importance to our further study and research for the operating system.

This thesis is about a Operating System which is developed from the blank,and its’ name is MyOS.This MyOS is simple in structure, however, it possesses the management of memery and task and basic input and out, it support the multitask and run under the 32 bit protect mode. In addition, it using both graphical interfaces and command line interfaces to interact with user.As for the API, the MyOS get some API for the graphical display and timer, by which we develop some program which may run on the MyOS.

This thesis are composed by these elements.First of all, it describe the hardware which Operating System run on, and then ,it present the fundamental character and main function of the Operating System. Secondly ,it illuminates the protect mode which is one of the two important elements of the thesis, and the other one is particularize the design and realization of the MyOS, before which ,it will briefly tell you the developing circumstance and the developing language. At last, it deals with the API of MyOS and the program developed based on these APIs.

Keywords: Operating System , Principle of Running ,32bit Protect Mode

II

目 录

摘 要 .......................................................................................................................................... I ABSTRACT ................................................................................................................................... II 第1章 绪论 ................................................................................................................................. 1

1.1 选题背景和意义 ............................................................................................................. 1 1.2 国内外研究现状 ............................................................................................................. 2 1.3 主要研究内容 ................................................................................................................. 3 第2章 操作系统运行环境及运行原理简述 ............................................................................. 4

2.1 计算机系统硬件组成简述 ............................................................................................. 4

2.1.1 总线 ....................................................................................................................... 4 2.1.2 I/O设备 ................................................................................................................. 4 2.1.3 主存 ....................................................................................................................... 5 2.1.4 处理器 ................................................................................................................... 5 2.2 操作系统的基本特性 ..................................................................................................... 5

2.2.1并发性 ...................................................................................................................... 6 2.2.2共享性 ...................................................................................................................... 6 2.2.3虚拟性 ...................................................................................................................... 6 2.2.4异步性 ...................................................................................................................... 7 2.3 操作系统的主要功能 ..................................................................................................... 7

2.3.1 处理机管理 ........................................................................................................... 7 2.3.2 存储器管理 ........................................................................................................... 7 2.3.3 设备管理 ............................................................................................................... 8 2.3.4 文件管理 ............................................................................................................... 8 2.3.5 用户接口 ............................................................................................................... 9 2.3.6 程序接口 ............................................................................................................... 9

第3章 32位保护模式介绍 ...................................................................................................... 11

3.1 32位保护模式简述 ...................................................................................................... 11

3.1.1 存储管理机制和地址转换机制简介 ................................................................. 11 3.1.2 保护机制 ............................................................................................................. 12 3.2 分段管理机制 ............................................................................................................... 15

3.2.1 段定义 ................................................................................................................. 15 3.2.2 虚拟地址到线性地址的转换机制 ..................................................................... 15 3.2.3 虚拟地址到线性地址的转换机制所需要的数据结构和寄存器 ..................... 16 3.3 分段管理机制所依赖的两类寄存器 ........................................................................... 17

3.3.1 控制寄存器 ......................................................................................................... 17 3.3.2 系统地址寄存器 ................................................................................................. 18 3.4 系统段描述符、门描述符、任务状态段 ................................................................... 20

3.4.1 系统段描述符 ..................................................................................................... 20 3.4.2 门描述符 ............................................................................................................. 21 3.4.3 任务状态段TSS ................................................................................................. 22 3.5 控制转移 ....................................................................................................................... 23

3.5.1 任务内无特权级变换的转移 ............................................................................. 23 3.5.2 任务内不同特权级的变换 ................................................................................. 24

1

3.5.3 任务间控制转移----“任务切换” .................................................................... 26

第4章 MyOS具体设计与实现 ............................................................................................... 28

4.1 MyOS的开发语言、开发流程以及开发环境简述 ................................................... 28 4.2 MyOS运行原理简述 ................................................................................................... 29

4.2.1 引导扇区 ............................................................................................................. 29 4.2.2 加载内核 ............................................................................................................. 30 4.2.3 执行系统内核 ..................................................................................................... 30 4.3 MyOS内核设计与实现 ............................................................................................... 31

4.3.1 内存管理 ............................................................................................................. 31 4.3.2 定时器设计 ......................................................................................................... 36 4.3.3 FIFO缓冲区设计 ............................................................................................... 41 4.3.4 基本输入输出管理 ............................................................................................. 43 4.3.5 多任务管理 ......................................................................................................... 46 4.3.6 图形界面的设计 ................................................................................................. 54 4.3.7 编程接口API设计 ............................................................................................ 56

第5章 利用MyOS的API编写程序并运行 .......................................................................... 60

5.1 表秒计时器 ................................................................................................................... 60 5.2 游戏“贪吃蛇” ........................................................................................................... 61 第6章 总结 ............................................................................................................................... 68 参考文献 ....................................................................................................................................... 69 致 谢 ....................................................................................................................................... 70

2

华北电力大学本科毕业设计(论文)

第1章 绪论

1.1 选题背景和意义

众所周知,一个成型的操作系统往往非常复杂,因为考虑到操作系统作为软硬件桥梁的特殊地位,其看上去比一般的软件系统更加的难以理解,因为其核心部分往往包含许多直接针对CPU,内存和I/O端口的操作。现在有许多公开源代码的操作系统,可供随时下载和阅读,但是如果没有些许实际开发的经验,往往会深陷代码的汪洋大海中,“一叶障目,不见泰山”,并且这些代码的细节之间经常互相关联,要理解起来很不容易。而动手开发一个简易的操作系统,从底层原理一步一步实现最基本和最简单的功能,到最后积累成一个简单的操作系统模型,最终达到为以后阅读大型操作系统(例如Linux)的源代码打下基础的目的。

作为一个计算机系并有志于从事计算机相关行业的大学生来说,很多本科时期计算机相关科目的学习都仅仅是“纸上谈兵”,缺乏实际操作,更谈不上实际应用,这对以后的深入学习和长期发展非常不利。例如:汇编课程学习的大部分内容都使用8086/8088的16位实模式作为学习蓝本,而现在操作系统因为大内存的使用, 基本全部是32位保护模式,虽然语法等基础没有变化,但是其32保护模式的架构思想在汇编课程中却没有提及到;作为系统开发利器的c语言,其课程学习只是掌握表面语法,如果没有深入地应用,没有研究其在机器层面的原理,很多精髓都无法掌握,也就谈不上真正掌握c语言;而对于学习操作系统本身,如果没有阅读经典操作系统的源代码或者尝试去做系统开发这方面的实践,对于其诸多概念和原理是无法深入理解的,只能停留在表面理解,无法做到心中有数,就更谈不上创新了。

本文试图通过这么一个“麻雀虽小,五脏俱全”操作系统的开发,达到复习并总结本科时期诸多科目的目的,包括:x86汇编语言,C语言,计算机组成原理,微机原理,数据结构,操作系统,而事实上,在基本完成毕设,撰写论文之际,对这几门课程的确有了更新更高层次的理解,很多当时只是浮于表面的概念现在都能够或多或少地落实到代码并能够亲自实现和完善,同时也初步具备了自己阅读和分析大型操作系统源代码的能力。

1

华北电力大学本科毕业设计(论文)

1.2 国内外研究现状

操作系统并不是与计算机硬件一起诞生的,它是在人们使用计算机的过程中,为了满足两大需求:提高资源利用率、增强计算机系统性能,伴随着计算机技术本身及其应用的日益发展,而逐步地形成和完善起来的。

操作系统的发展经历了手工操作(无操作系统)、批处理系统、多道程序系统、分时系

统、实时系统、通用操作系统。如今,概念意义上的操作系统和通俗意义上的操作系统差距越来越大,通俗意义上的操作系统为了方便而把最普通的包和应用程序的集合包括在操作系统内,而随着操作系统的发展,一些功能更强的“第二类”操作系统软件也被包括进去。在今天,没有图形界面和各种文件浏览器已经不能称为一个真正的操作系统了。

另一方面,操作系统的发展和计算机硬件的发展紧密联系,而从计算机硬件发展的角度来看,操作系统的发展经过了大型机时代操作系统、小型机时代操作系统(Unix)和个人计算机时代操作系统。

大型机时代。早期的操作系统非常多样化,生产商生产出针对各自硬件的系统。每一个操作系统都有很不同的命令模式、操作过程和调试工具,即使它们来自同一个生产商。最能 反映这一状况的是,厂家每生产一台新的机器都会配备一套新的操作系统。这种情况一直持续到二十世纪六十年代IBM公司开发了System/360系列机器。尽管这些机器在性能上有明显的差异,但是他们有统一的操作系统——OS/360。OS/360的成功陆续地催化出MFT、MVT、SVS、MVS、MVS/XA、MVS/ESA、OS/390和z/OS。

小型机和UNIX的崛起。UNIX操作系统是由AT&T公司开发出来的。由于它的早期版本是完全免费的,可以轻易获得并随意修改,所以它得到了广泛的接受。后来,它成为开发小型机操作系统的起点。由于早期的广泛应用,它已经成为的操作系统的典范。不过,它始终属于AT&T公司,只有那些能负担的起许可费的企业才用得起,这限制了它的应用范围。

早期的操作系统是可以被用户软件所利用的功能的集合。一些有能力的公司发展更好的系统,但他们不支持其他公司硬件的特性。

60年代末70年代初,几种硬件支持相似的或提供端口的软件可在多种系统上运行。早期的系统已经利用微程序来在他们的系统上实现功能。事实上,除了360/165和360/168外,360/40之后的大部分360系列的机器都实行微程序设计。

个人计算机时代。Apple, DOS 和以后微型处理器的发展使计算机的应用普及至中小企及个人爱好者。而计算机的普及又推动了硬件组件公共接口的发展(如S-100,SS-50,Apple

2

华北电力大学本科毕业设计(论文)

II,ISA和PCI总线),并逐渐地要求有一种“标准”的操作系统去控制它们。在这些早期的计算机中,主要的操作系统是8080/8085/Z-80 CPU用的Digital Research’s CP/M-80,它建立在数码设备公司(Digital Research)几个操作系统的基础上,主要针对PDP-11架构。在此基础上又产生了MS-DOS(或IBM公司的PC-DOS)。这些计算机在ROM(只读存储器)都有一个小小的启动程序,可以把操作系统从磁盘装载到内存。IBM-PC系列的BIOS是这一思想的延伸。自1981年第一台IBM-PC诞生以来,BIOS的功能得到不断地增强。

随着显示设备和处理其成本的降低,很多操作系统都开始提供图形用户界面。如:许多UNIX提供的X Window一类的系统、微软的Windows系统、苹果公司的Mac系统和IBM公司的OS/2等。最初的图形用户界面是由Xerox Palo Alto研究中心70年代初期研发出来的,之后被许多公司模仿,继承发展。

1.3 主要研究内容

1. 实现一个能够在裸机上运行的简易的操作系统。

? 该操作系统在保护模式下运行,是一个多任务的操作系统,可以进行任务调度

以及任务之间的通信。

? 该操作系统具有自己的硬盘驱动程序,并实现一个简易的文件系统,能够实现

基本的文件操作系统调用。

? 该操作系统具有简单的内存管理功能。

? 该操作系统使用键盘和显示器作为基本的输入输出系统。

? 该操作系统具有简易的命令行风格的用户界面,实现一些主要的操作系统命令。 2. 在时间允许的情况下,进一步改善用户界面,能够实现一个简易的图形用户界面,

可以使用鼠标进行操作,并能够输出声音(选做)。

3. 将已完成的简易操作系统制作成软盘镜像文件,可在虚拟机中运行。 4. 将已完成的简易操作系统制作成光盘在真机上运行(选做)。 5. 撰写毕业论文。 6. 答辩。

3

华北电力大学本科毕业设计(论文)

第2章 操作系统运行环境及运行原理简述

2.1 计算机系统硬件组成简述

如图2-1所示,这是一个计算机系统硬件组成的简单模型。

鼠标 键盘 显示器 磁盘 USB控制器 图形适配器 I/O总线 寄存器文件 PC ALU 主存储器 总线接口 CPU 系统总线 I/O桥 存储器总线 磁盘控制器 扩展插槽 图2-1 计算机系统硬件组成的简单模型 2.1.1 总线

贯穿整个系统的是一组电子管道,称作“总线”,它携带信息字节并负责在各个部件之间传递。通常总线被设计成传送定长的字节块,也就是“字”(word)。一个字的字节数(被称为“字长”)是一个基本的系统参数,在各个系统中可能不相同。现在的很多机器字长有的是4个字节(即32bit),有的是8个字节(即64bit)。[1]

2.1.2 I/O设备

输入/输出设备(I/O设备)是系统与外部世界的联系通道。如图2-1所示,示例系统包括4个I/O设备:作为用户输入的键盘和鼠标,作为用户输出的显示器,以及用于长期存储数据和程序的磁盘驱动。

每个I/O设备都通过一个“控制器”或者“适配器”与I/O总线相连。“控制器”和“适配器”之间的主要区别是它们的封装方式。“控制器”是置于I/O设备本身或者系统的主板上的芯片组内,而“适配器”是是一块插在主板插槽上的卡。但是这两者的任务一样的,它们都负责在I/O总线和I/O设备自检传递和翻译信息。

4

华北电力大学本科毕业设计(论文)

2.1.3 主存

主存是一个临时存储设备,在处理器执行程序时,用来存放程序和程序所处理的数据。 从物理组成的角度来说,主存是一组动态随机存取存储器芯片组成的,从逻辑来说,

主存就是一个线性的字节数组,每一个字节都有唯一的地址,这些地址从0开始。一般来说,组成程序的每条机器指令都由不同数量的字节构成,与C程序变量相对应的数据项的大小是根据类型变化的。

2.1.4 处理器

中央处理单元(CPU),简称处理器,是解释和执行存储在主存中指令的引擎。处理器的核心是一个字长的存储设备,称为程序计数器(PC),在任何时刻,PC都指向主存中的某条机器语言指令。

从系统通电开始,直到系统断电,处理器都一直在不断地执行程序计数器所指向的指令,再更新程序计数器,使其指向下一条需要执行的指令,这条指令不一定和上一条执行的指令相邻。处理器看上去是按照一个非常简单的指令执行模型来操作的,这个模型是由指令集结构决定的。

这样的简单操作并不多,而且操作是围绕主存、寄存器文件、和算术逻辑单元(ALU)进行的。寄存器文件是一个小的但是高速的存储设备,由一些1字长的寄存器组成,每个寄存器有唯一的名字和特别的使用方式。而ALU则负责计算,计算数据和地址。

下面是一些简单操作的例子,可以通过这些例子更好地了解CPU的各个部件在CPU工a) 加载:把一个字节(或者一个字)从主存复制到寄存器,寄存器里原来的值被覆盖。

b) 存储:把一个字节(或者一个字)从寄存器复制到主存的某个位置,以覆盖内存中该位置原来的值。

c) 计算:把两个寄存器的内容复制到ALU,ALU对这两个字做算术运算,并将结果存在一个寄存器中,寄存器里原来的值被覆盖。

d) 跳转:从指令中抽取一个字,并将这个字复制到程序计数器(PC)中,实现跳转。 e) 处理器看上去指示它的指令集结构的简单实现,但是实际上现代处理器使用了非常复杂的机制来加速程序的执行。所以,可以这样区分“处理器的指令集结构”和“微体系结构”:“指令集结构描述的是每条机器代码指令的效果”,而“微体系结构”描述的是处理器实际上是如何实现的。

作的作用和工作原理。CPU在执行的要求下,可能会执行以下操作:

2.2 操作系统的基本特性

操作系统的基本特性有并发性、虚拟性、共享性、异步性,其中,并发性和虚拟性是操作系统最重要的两个特性。

5

华北电力大学本科毕业设计(论文)

2.2.1并发性

并行性和并发性是既相似又有区别的两个概念,并行性是指两个或者多个事件在同一时刻发生,而并发性是指两个或者多个事件在同一时间间隔内发生。

为了使多个程序能够在系统中并发执行,系统必须为每个程序建立进程(Process)。简单来说,进程是指在系统中能独立运行并作为资源分配的基本单位,由一组机器指令、数据和堆栈等组成,是一个能独立运行的活动实体。多个进程之间可以并发执行和交换信息。一个进程在运行时需要一定的资源,如CPU、存储空间以及I/O设备等。

操作系统中程序的并发执行将使系统复杂化,以至于在系统中必须增设若干新的功能模块,分别用于对CPU、内存、I/O设备以及文件系统等重要资源进行管理,并控制好系统中各个进程的运行。

长期以来,进程都是操作系统中可以拥有资源并作为独立运行的基本单位。当一个进程因故不能继续运行,操作系统便调度另一个程序运行。但是由于进程拥有自己的资源,故使得调度付出的代价非常大,所以人们引入比进程更小的单位——线程。通常一个进程中包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的OS中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统转,所以对地进行调度所花费的系统资源很少,从而能够更高效地提高系统内多个程序并发执行的程度。

2.2.2共享性

在操作系统中,所谓共享,是指系统中的资源可供内村中并发执行的进程共同使用,相应的,把这种西苑共同使用成为资源共享,或者是资源复用。由于资源的属性不同,进程对资源的复用方式也不同,主要实现资源共享的方式有以下两种:互斥共享方式和同时访问方式。

2.2.3虚拟性

操作系统中的所谓“虚拟性”,是指通过虚拟技术把一个物理实体变为若干逻辑上的对应物。物理实体是实际存在的,而逻辑对应物是现对于用户来说的,是用户感觉到的可以使用的东西。这个技术的实现主要有两种途径,时分复用技术和空分复用技术。

通俗地理解,时分复用技术就是将物理设备的使用机会按时间划分为许多小段,各个用户(或进程)在调度进程的调度下,交替使用这些使用设备的机会,常见的例子有:系统通过对处理机进行分片,然后把时间片分配给各个进程,实现了把一台物理上的处理机虚拟位多台逻辑上的处理机,从而在每台逻辑处理机上运行一道程序。通常把用户感觉到的处理机成为虚拟处理机。

而空分复用技术则被用来提高存储空间的利用率,常见的有虚拟磁盘技术和虚拟存储器技术。

6

华北电力大学本科毕业设计(论文)

2.2.4异步性

在多道程序下允许多个进程并发执行,但是只有进程获得所需的资源后方能执行。在单处理机环境下,由于系统只有一台处理机,所有每个时刻只能有一个进程运行,其他的必须等待处理机。当正在执行的进程提出某种资源要求时,此项资源如果得不到满足,这个进程就无法继续下去。可见一个进程在系统中的运行不可能是“一帆风顺”的,而是以“走走停停”的方式运行,而且这种方式在进程运行之前是不可预见的,具有随机性,并且由于各个任务、系统、机器的之间的差异,进程通常以人们不可预知的速度向前推进,这就是进程的“异步性”。

为了应对这种进程的“异步性”,为了让系统中的各个进程都不出错地执行完自己的任务,操作系统必须具备完善的进程同步机制,使得进程之间能够协调地利用好共享的资源,获得最后想要的结果。

2.3 操作系统的主要功能

操作系统的主要任务,是为多道程序的运行提供良好的运行环境,以保证运行在系统中的各个进程能够有序、高效地运行,并能够最大程度地提高系统中各项资源的利用率,而考虑到现在的系统和人之间的交互愈发频繁,操作系统还需提供给用户的使用接口和给程序员的编程接口。

所以,操作系统应该具备这样几个主要功能:处理机管理,存储器管理,设备管理,文件管理,用户接口和程序接口。而随着网络的普及,现代操作系统一般都具备网络功能。

2.3.1 处理机管理

在未引入线程的系统中,处理机的分配和运行都是以进程为基本单位,因而对处理机的管理可以归纳为对进程的管理:创建和撤销进程(进程控制),对各个进程的运行进行协调(进程同步),进实现进程之间的信息交换(线程通信),按照一定的算法将处理机的处理时间分配给进程。

其中进程同步包括“进程互斥”和“进程同步”,“进程互斥”是指各个进程在对临界资源进行访问时,应采用互斥方式,“进程同步”是指相互合作去完成某项任务的进程之间,必须遵循某种顺序,操作系统中有同步机构对它们的执行次序加以协调。

2.3.2 存储器管理

存储器管理管理的主要任务是为多道程序的运行提供良好的环境,方便用户使用存储器,提高存储器的利用率以及能从逻辑上扩充内存。主要功能包括:内存分配、内存保护、地址映射、内存扩充。

其中,为了实现内存分配,在内存分配机制中应该有这样的设计和功能:用于实现内存分配的数据结构,内存分配功能,内存回收功能。

7

华北电力大学本科毕业设计(论文)

而内存保护的的主要任务是保证让每个程序都只能在属于自己的内存空间运行,不能访问到别的程序的空间,更不能够访问到操作系统的内存空间。此时,让内存保护得以实现,必须设置合理的内存保护机制。

内存映射功能主要是实现将程序的“逻辑地址”或“相对地址”映射为内存的“物理地址”。

内存扩充并非是扩大物理内存的实际容量,而是借助于虚拟存储技术,从逻辑上扩充内存容量,使用户能够使用的内存容量大于实际的物理内存容量。内存扩充机制主要包括:请求调入功能和置换功能。

2.3.3 设备管理

设备管理用于管理计算机系统中所有的外围设备,而设备管理的主要任务是:完成进程提出的I/O请求;为进程分配其所需要的I/O设备;提高CPU和I/O设备的利用率。

为了实现上述任务,设备管理应具备缓冲管理、设备分配、设备处理和虚拟设备等功能。

缓冲管理集中要解决的矛盾是,CPU运行的高速型和外围设备的低速性。而随着CPU速率的不断提高,而I/O又具备一定的确定性,所以这个矛盾更加严重地降低了CPU的利用率。为了提高CPU的利用率,可以在I/O设备和CPU之间设置缓冲区,用于实现提高CPU的利用率和系统吞吐量。

设备分配的任务是根据进程的I/O请求和资源情况,按照某种分配策略,为进程分配所需要的设备。如果在I/O设备和CPU之间还存在着设备控制器和I/O通道,还要未分配出去的设备分配对应的控制器和通道。为了实现设备分配,系统中应设置设备控制表、控制器控制表等数据结构,用于记录设备及控制器的标示符和状态,系统应该以这些数据结构的实时数据为依据,了解制定设备当前是否可用,是否忙碌,以供进行设备分配。

设备处理其实就是设备驱动程序,基本任务是用于实现CPU和设备控制器之间的通信,即由CPU向设备控制器发I/O命令,要求其完成指定的I/O操作;反之,由CPU接受从控制器发来的中断请求,并迅速给予响应和相应的处理。

2.3.4 文件管理

在现代计算机系统中,一般将程序和数据以文件的形式存储在磁盘或者磁带上,供所有的或者某些指定的用户使用。为了达到这个目的,操作系统必须配置文件管理机构,文件管理的主要任务是对用户文件盒系统文件进行管理,以方便用户使用,同时需要保证文件的安全性。所以,文件管理的功能主要有:对文件存储管理空间的管理、目录管理、文件的读/写管理、文件的共享与保护。

为了实现文件存储空间的管理,系统应设置相应的数据结构,用于记录文件存储空间的使用情况,以供分配存储空间时作为参考。系统还应具有对存储空间进行分配和回收的

8

华北电力大学本科毕业设计(论文)

功能。为了提高存储空间的利用率,通常采用离散分配方式对存储空间进行分配,以减少外村零头,以盘块为基本分配单位,盘块的大小通常为1—8KB。

为了实现用户能方便的在外存上找到自己的文件,通常由文件系统为每个文件建立一个目录项。目录项包括文件名、文件属性、文件在磁盘上的物理位置等等描述文件的信息。而若干个目录项又可以构成一个目录文件。目录管理的主要任务是为每个文件建立目录项,并对所有的目录项进行有效的管理,以方便按名存取,即用户只需要提供文件名就可以对该文件进行存取。

文件的读/写管理实际上是简化用户对文件的操作,当用户想要打开、添加、修改、删除一个文件时,操作系统先根据用户给出的文件名去检索文件目录,从中获得文件在外存的位置,然后利用读/写指针,对文件进行读或写。

文件的保护是系统为用户的文件提供保护,以达到这几个目标:防止没有权限的用户存取文件,防止冒名顶替存取文件,防止以不正确的方式使用文件。

2.3.5 用户接口

用户接口是提供给用户使用的接口,用户可以通过该接口取得操作系统的服务。设置用户接口的目的是为了方便用户直接或者间接地控制自己的作业,用户可以通过这个接口向作业发出命令以控制作业的运行。用户接口分为联机用户接口和脱机用户接口,联机用户接口又分为命令行接口和图形接口。

命令行接口是由一组键盘操作命令组成,当用户在终端或者控制台上每次键入一条命令后,系统便立即转入命令解释程序,对这条命令加以解释并执行该命令。

图形接口是为了解决用户必须熟记各种命令的名字和格式的问题而生的,其采用图形化的操作界面,用非常容易识别的各种图标来将系统的各项功能、各种应用程序和文件形象逼真地表现出来,用户不需要用键盘输入命令,只需要用鼠标点击就能够控制操作系统。

联机用户接口都能够实现用户与进程的实时交互,而脱机用户接口则是为批处理作业的用户提供的,故也可以称作是批处理用户接口。批处理作业的用户不能直接与自己的作业交互作用,只能委托系统代替用户对作业进行控制和干预。

2.3.6 程序接口

程序接口是为用户程序在执行中访问系统资源而设置的,是用户所编制的程序取得操程序接口的本质是一组系统调用,每一个系统调用都是一个能完成特定功能的子程序,早期版本的系统调用都是用汇编语言提供的, 只有在用汇编语言书写的程序中才能直接使用系统调用;但在高级语言以及C语言中,玩玩提供了与各系统调用一一对应的库函数,这样应用程序就可以通过调用对应的库函数来来使用系统调用。

9

作系统服务的唯一途径。

每当应用程序要求OS提供某种服务时,便调用具有相应功能的系统调用。

华北电力大学本科毕业设计(论文)

与早期操作系统系统调用不同的是,近几年的操作系统中,系统调用本省已经采用C语言进行编写,并以函数的形式提供,所以,在C语言编制的程序中,可以直接使用系统调用。

10

华北电力大学本科毕业设计(论文)

第3章 32位保护模式介绍

80386以上的X86处理器有两种工作模式:实模式和保护模式。尽管实模式下的80386的功能要大大超过先前处理器(8086/8088,80186,80286),但只有在保护模式下,80386才能真正发挥作用。

3.1 32位保护模式简述

32位保护模式相对于实模式的优势体现在这几个方面:在保护模式下,80386处理器的32根地址线全部有效,可寻址达到4GB的物理地址空间;保护模式所独有的存储器分段管理机制和可选的存储器分页管理机制,前者为存储器的共享和保护提供了硬件支持,后者为实现虚拟存储器提供了硬件支持;支持多任务,在保护模式下,80386处理器给予系统开发者极大便利,可以快速地进行任务切换和保护任务环境;80386处理器有4个特权级和完善的特权检查机制,既能高效的实现资源共享,又能保证代码及数据的安全和保密,以及任务之间的隔离;在保护模式下可以用虚拟8086方式来执行8086程序。

3.1.1 存储管理机制和地址转换机制简介

为了对存储器中的程序和数据实现保护和共享提供硬件支持,80386在保护模式下采用了扩充的存储器分段管理机制;为了对实现虚拟存储器提供硬件支持,80386在保护模式下提供了可选的存储器分页管理机制。这些存储管理机制有80386的存储器管理部件MMU实现。

由于本文实现的操作系统没有使用更高级更复杂的分页管理机制,所以在这里重点介绍与分段有关的地址转换,不详细介绍分页管理机制。

保护模式下的虚拟存储器由大小可变的存储块构成,这样的存储块称为段。80386采用称为描述符的数据来描述段的位置、大小、使用情况以及其他属性。虚拟存储器的地址(即“逻辑地址”)由指示描述符的“选择子”和“偏移地址”两部分构成,这样的地址集合称为“虚拟地址空间”。

很显然,只有在物理存储器中的程序才能运行,只有在物理存储器中的数据才能访问,一切的地址都要映射到物理地址,二维的虚拟地址(“选择子”+“偏移地址”)必须转换成为一维的物理地址(实际内存的地址)。由于物理地址远小于虚拟地址,所以只有一部分虚拟地址空间能真正映射到物理存储器。

因为每一个任务都有一个虚拟地址空间,而为了避免多个并行任务的多个虚拟空间直接映射到一个物理地址空间,所以,采用线性地址空间隔离虚拟地址空间和物理地址空间。线性地址空间和物理地址空间对等,大小一样。

如图3-1所示。80386分两步实现虚拟地址空间到物理地址空间的映射。第一步是通过虚拟地址中的“选择子”在描述符表GDT中查询得到段描述符,在段描述符中记载着这

11

华北电力大学本科毕业设计(论文)

个段的段起始地址(在8086的实模式下,段起始地址是直接存储在段寄存器中),再结合“偏移地址”计算得到线性地址。第二步是可选的,如果采用分页机制,就用MMU来将线性地址映射为物理地址,从而寻址结束。所以,如果没有采用分页管理机制,线性地址空间等于物理地址空间。

选择子(16位):偏移量(32位) 虚拟地址 分段管理机制 分页管理机制 不启用分页管理机制 图 3-1 虚拟地址空间到物理地址空间的映射

32位 线性地址 32位 物理地址

3.1.2 保护机制

为了支持多任务,对各任务实施保护是必要的。保护机制必须有效地实现不同任务之间的保护和同一任务内的保护 3.1.2.1不同任务之间的保护

不同任务之间的保护中的一个重要方面是应用程序之间的保护。是通过把每个任务放置在不同的虚拟地址空间的方法来实现任务与任务之间的隔离,达到应用程序之间保护的目的。虚拟地址到物理地址的映射函数在每个任务中单独定义,随着任务的切换,映射函数也随之切换,这样的机制使得任务A和任务B拥有相同的虚拟地址空间,但是映射到物理空间后,就是两块独立不相干的区域。这样,两个不同的任务,尽管虚拟存储单元地址相同,但实际的物理存储单元地址不同。 3.1.2.2同一任务内的保护

实现在同一任务内的保护需要达到那些目标?怎样实现在同一任务内的保护?为什么要实现在同一任务内的保护?实现在同一任务内的保护,主要目标是区分不同的段的特权级。在一个任务内,可以定义四种执行特权级别,用于限制对任务中段进行访问。一般会按照不同的段中的数据和代码的可信任程度,给予不同的段不同的特权级。例如:具有最高特权级别的数据,只能由最可信任的代码访问;给不重要的数据段和代码段分配比较低的特权级别;具有最低特权级别的数据,可以被任何特权级的代码访问到。

特权级的典型用法如图3-2所示,把操作系统的核心放在0级,操作系统的其余部分(操作系统外围程序)放在1级,中间软件(数据库和办公软件)放在2级,用户的应用程序放在3级。这样安排,使得在0级的操作系统核心有权利访问所有特权级中的所有数据段和代码段,在1级的操作系统其余部分有权利访问除了0级意外的所有代码段和数据段,而在3级中的应用程序只能访问本任务的数据段和代码段。

12

华北电力大学本科毕业设计(论文)

图 2-2 四个特权级的分配

Code of Program 2 Code of Kernel Code of OS Code of OS Data of Program 2 Data of Kernel Data of OS Data of OS Code of Program1 Data of Program 1 3.1.2.3实现“同一任务内的保护”所依赖的机制

在程序运行中,32位保护模式依靠CPL(current privilege level)、DPL(descriptor privilege level)和RPL(requestor privilege level)三种数据结构来实施任务内的特权级保护,这三种数据结构如图3-3所示。

图 3-3 CPL、DPL、RPL的数据结构示意图

这些数据结构的具体解释如下:

a) 当前特权级CPL。当前代码段的选择子中,最低2位为特权级CPL,这表示当前正在运行的程序的特权级。CPL是程序本身的性质所决定的:如果是“操作系统和操作

13

华北电力大学本科毕业设计(论文)

系统核心程序”(如存储器管理程序、任务调度程序、访问控制程序),那么,特权级为0;如果是“操作系统外围的系统程序”(如缓冲区分配程序、外设驱动程序),那么,特权级为1;如果是一些“应用软件”(如数据库、办公软件),那么、特权级为2;如果是用户程序,那么特权级为3。

b) 描述符特权级DPL。每个段的段描述符中,用描述符特权级DPL表示此段的特权级。程序运行时,数据段的DPL在每次被访问时受到检查,以确定程序是否有权访问该数据段。

c) 请求特权级RPL。数据段的段选择子中的最低2位为请求特权级RPL,数据段总是被代码访问的,此时RPL将受到检测,以确定此次访问是否合法。只有当代码段的CPL的级别不低于RPL的时候,才能是访问成功进行。数据段中设置RPL是为了防止特权级低的程序访问特权级高的数据段。

一般情况下,数据段选择子中的RPL和此段描述符中的DPL是相等的,由于在程序运行中,要访问数据时,总是先装入选择子,再由选择子找到段描述符,然后由段描述符找到对应段,所以,提前在装入选择子这一环用RPL进行特权级保护检查,使得保护机制更加快捷。当一个程序访问数据段的时候,其CPL的级别必须高于或等于被访问段的DPL。例如,CPL为0的代码段对任何特权级的数据段都可以访问,而CPL为1的代码段只能访问特权级为1、2、3的数据段。

在程序运行过程当中,除了数据读写操作时进行界限检查之外,当遇到转移指令JMP、中断指令INT、电泳指令CALL、和返回指令RET时,可能因为涉及到不同的段,所以也会进行界限检查。如果是段内转移,则保护模式会检查这些指令的目标地址是否超出了当前代码段的界限,如果是段间转移,则会对目标端的特权级进行检查,只有CPL级别低于或者等于目标端的DPL时,才能实现转移,也就是说,转移,总是往高级别或者相同级别的目标段进行。如此一来,出现了CPL和DPL状态不同的情况,这种情况产生在异常中断和调用INT指令、CALL指令的时候,此时,CPL改成DPL的值,CPL数值减小,级别提高。但是,当执行RET、IRET指令时,情况和执行CALL、INT指令相反,此时,CPL的数值可能会增大,级别降低。

最后,需要注意的是:

a) DPL是“段描述符”提供的,每个段都对应着一个DPL,而CPL和RPL是“选择子”

提供的。

b) CPL对应于代码段,而RPL对应于数据段

c) 一个代码段处于运行过程中,没有发生段间转移时,CPL总是等于DPL

d) CPL为0时,可以访问任何RPL级别的数据段,CPL为1时,只能访问RPL为1、

2、3的数据段。。。以此类推。

14

华北电力大学本科毕业设计(论文)

3.2 分段管理机制

上面讲到存储管理机制和虚拟地址映射物理地址机制,那在32位保护模式下,操作系统具体怎样实现这些机制?这节的主要内容就是:介绍保护模式下的段定义,以及怎样实现由“段选择子”+“段内偏移”构成的“二维虚拟地址”转换为“一维的线性地址”。

3.2.1 段定义

段是实现虚拟地址到线性地址转换机制的基础。在保护方式下,每个段由这三个参数定义:段基地址,段界限,段属性。通俗点说,“段基地址”规定了这个段在“线性地址空间”中的开始地址,“段界限”规定这个段的大小,值得注意的是,在80386保护模式下,段界限仅仅由20位来表示,大家都知道,20位能表达的最大地址是1M,如果是以1B作为单位的话,一个段的最大地址范围是1M*1B = 1MB,显然无法满足程序的需求,而且也体现不出保护模式相对于实模式的优越性,为了解决这个问题,在“段属性”(定义段的三个元素之一)中用一位“粒度位”(用符号G标记)来规定20位段界限的单位,如果G=0表示以1B作为单位,段大小的范围为1B-1M;如果G=1表示以4KB为单位,段大小的范围为4KB-4GB。“段属性”规定了段的主要特性,例如上面提到的“粒度位”,以及段的特权级,这里不详细介绍。

3.2.2 虚拟地址到线性地址的转换机制

由上一个小节可知,由“二维的虚拟地址”到“一维的线性地址”的转换机制可由图3-4所示。

15

图 3-4 由虚拟地址到线性地址的转换机制

华北电力大学本科毕业设计(论文)

3.2.3 虚拟地址到线性地址的转换机制所需要的数据结构和寄存器

3.2.3.1段描述符

上面讲到三个参数可以定义一个段,这三个参数由一个8字节 = 64位的数据结构保存,这个数据结构称作“段描述符”(顾名思义),其中32位表示“段基地址”,20位表示“段界限”,剩下的12位表示“段属性”。为了和上一代的机器兼容,32位的“段基地址”和20位的“段界限”并非连续存放,具体怎样存放,“段属性”中哪一位代表什么,限于论文篇幅,这里不详细介绍。 3.2.3.2(全局和局部)段描述符表

一个任务会涉及到多个段,每个段需要一个描述符来描述,为了方便管理和查询,80386将描述符组织成线性表,由描述符组成的线性表称作“描述符表”。

在80386中一共有三种“描述符”表:全局描述符表GDT(Global Descriptor Table),局部描述符表LDT(Local Descriptor Table),中断描述符表IDT(Interrupt Descriptor Table)。在整个系统中,全局描述符表GDT和中断描述符表IDT只有一张,局部描述符表由多张,每个任务可以有一张自己的LDT。

每个描述符表本身形成一个特殊的数据段,这样的特殊数据段最多可以包含8096个描述符。

每个任务的“局部描述符表LDT”含有该任务自己的代码段、数据段、和堆栈段的描述符,也包含该任务所使用的一些“门描述符”,如:“任务门”和“调用门”门描述符。随着任务的切换,系统当前的“局部描述符表LDT”也随之切换。

“全局描述符表GDT”含有每一个任务都有可能或者可以访问的段的描述符,通常包括操作系统所使用的代码段、数据段、堆栈段的描述符,也包含多种特殊数据段描述符,例如各个用于描述任务LDT的特殊数据段。在任务切换时,不切换GDT。

通过LDT可以使各个任务私有的各个段与其他任务隔离,从而达到受保护的目的。通过GDT可以使各个任务都需要使用的段能够被共享。 3.2.3.3段选择子

由上面的内容可知“虚拟地址”是“二维”的,那是哪两个“维”呢?“虚拟地址”是由那两个东西组成的呢?在实模式中,这两个东西一个是保存在段寄存器(CS,DS,SS。。)中的段地址,另一个是段内偏移地址,此时线性地址 = (段寄存器)*16 + 段内偏移。而在保护模式中,“段选择子”代替了保存在段寄存器(CS,DS,SS。。)中的段地址。通俗点说,“段选择子”是一个索引,一个指针,它可以帮助操作系统在“段描述符表”中查找到定义这个段的“段描述符”,从而得到“段描述符”中的“段基地址”,其实到了这个时候,这个“段描述符”中的“段基址地址”,就和实模式中的段寄存器(CS,DS,SS。。)中的段地址有点相似了,但是不同点在于计算线性地址的方式,实模式中,线性地址 = (段寄存器)*16 + 段内偏移,而保护模式中,线性地址 = 段基地址 + 偏移。

“段选择子”长16位,第0位和第1位十请求特权级RPL(Request Privilege Level)

16

华北电力大学本科毕业设计(论文)

用于特权检查;第2位是引用描述符表指示位TI(Table Indirector),TI = 0指示从全局描述符表GDT中读取段描述符,TI = 1指示从局部描述符表LDT中读取段描述符;第3至15位一共13位用来指示“描述符索引”,由于每个描述符在描述符表中占据8个字节,所以,如果屏蔽掉段选择子的低三位(即令低三位为0),刚好可以得到当前描述符相对于第一个描述符的偏移地址,这或许是安排选择子高13位作为“描述符索引”的原因。

一共有13位作为选择子的“描述符索引”,所以一个描述符表中,最多有8096个描述符。

所以,总结来说就是,“二维的虚拟地址” = “选择子:偏移地址”,“选择子”+“段描述符表”得到“段描述符”,从“段描述符”得到“段基地址”,由“段基地址”和“偏移地址”就可以就算出线性空间中的“一维的线性地址”。 3.2.3.4段描述符高速缓冲寄存器

从上述叙述可知,在保护模式下,每一次由“虚拟地址”映射为“线性地址”时,都必须使用“选择子”查询“描述符表”来得到段描述符中的基地址。为了避免每一次存储器访问时,都要访问描述符表,从80286开始,每个段寄存器都配有一个高速缓冲寄存器,称之为“段描述符高速缓冲寄存器”或称为“描述符投影寄存器”,这个寄存器的存在对程序员来说是不可见的。

其工作原理为:每当把一个“选择子”装入到某个段寄存器时,处理器自动从描述符表中取出相应的描述符,把描述符中的信息装入到对应的高速缓冲寄存器中,此后在对该段访问时,处理器都是用对应高速缓冲寄存器中的描述符信息,而不用再从描述符表中取描述符。而绝大多数情况下,对存储器的访问都是在“对应选择子”装入到段寄存器之后进行的,所以,使用段描述符高速缓冲寄存器可以得到很好的执行效果。

3.3 分段管理机制所依赖的两类寄存器

上面的内容对80386的保护模式下的分段管理机制做了详细说明,但是有几点疑问,CPU靠什么来控制这些机制的实施?例如,怎么设置CPU,让其运行在实模式下或者保护模式下?还有,上面提到了可以保存8096个描述符的描述符表,这个表到底在内存的哪里,CPU怎么找到保存这个表的特殊数据段?

要回答这些问题,必须搞明白的一点是,为了使得分段管理机制的实施,CPU必须知道人们到底想让它“干什么”和“怎么干”。而通过设置“控制寄存器”,可以让CPU知道自己“走什么路”,按什么方式运行;通过设置“系统地址寄存器”,可以给予CPU一些实施这个机制必须的数据。

下面就对这两类寄存器进行详细说明。

3.3.1 控制寄存器

控制寄存器包含四个32位的寄存器,分别命名为CR0,CR1,CR2,CR3。

17

华北电力大学本科毕业设计(论文)

CR0的第0位为PE位,第31位为PG位,这两个控制位控制分段和分页管理机制,所

以称之为“保护控制位”。PE控制分段管理机制,PE = 0,处理器运行于实模式,PE = 1,处理器运行于保护模式。PG控制分页管理机制,PG = 0,禁用分页管理机制,此时“线性地址”等同于“物理地址”,PG = 1,启用分页管理机制,此时“线性地址”通过内存中的MMU转化为“物理地址”。CR0中的第1至4位为MP(算数存在位)、EM(模拟位)、TS(任务切换位)、ET(扩展类型类),它们共同控制浮点协处理器的操作,所以称为“协处理器控制位”。

CR1被保留,供以后的开发的处理使用,在80386中不能使用CR1,否则引起无效指令操作异常。CR2和CR3用于分页管理机制使用,由于这次的操作系统没有用到分页管理,限于论文篇幅,这里不做介绍。

3.3.2 系统地址寄存器

“全局描述符表GDT”、“局部描述符表LDT”和“中断描述符表IDT”在上文已经有所有讲述,这三个描述符表是保护模式下非常重要的数据结构,它们被保存在特殊数据段里,但是操作系统怎样快速地定位,找到它们,这就用到了“系统地址寄存器”。

一般来说,要定义一个段需要三个参数,段基地址、段大小、段属性,而由于这三个表太过于“著名”且“地位特殊”,已经不需要太多的“段属性”来对他们进行说明,所以,“系统地址寄存器”只负责告知这些特殊数据段的基地址和界限(即大小)。

这些“系统地址寄存器”包括“全局描述符表寄存器GDTR”(GDT Register)、“局部描述符表寄存器LDTR”(LDT Register)、“中断描述符表寄存器IDTR”(IDT Register)、任务状态段寄存器TR(TTS Register)。

图 3-5 控制寄存器和系统地址寄存器

18

华北电力大学本科毕业设计(论文)

3.3.2.1 全局描述符表寄存器GDTR

如图3-6所示,GDTR长48位,其中高32位作为基地址,低16位作为界限。GDTR中的段界限以1B为单位,由于段选择子中用13位作为描述符索引,所以一个描述符表最多有2的13次方个段描述符,每个段描述符占用8B空间,所以,用16位作为界限足够。通常对于含有N个描述符的描述符表的段界限应该设置为8*N – 1。

图 3-6 GDTR中的基地址和界限

3.3.2.2 局部描述符表寄存器LDTR

局部描述符表寄存器LDTR规定当前任务使用的局部描述符表LDT,如图所示,由程序实际上,每个任务的局部描述符表作为系统的一个特殊段,由一个全局描述符描述(当员可见的16位的寄存器和32位的程序员不可见的高速缓冲寄存器组成。

然其实没有“全局描述符”这个说法),这个全局描述符存放在GDT中。在初始化或任务切换过程中,把指示描述当前任务或者切换后任务LDT的描述符的选择子装入到LDTR(即上面提到的程序员可见的16位的寄存器),处理器根据装入LDTR可见部分的选择子,从GDT中去除对应的描述符,并把LDT的基地址和界限等信息保存到LDTR对应的高速缓冲寄存器中(32位的程序员不可见的高速缓冲寄存器)。随后对LDT的访问,既可以根据保存在高速缓冲寄存器中的有关信息进行合法性检查。

LDTR寄存器包含当前任务LDT的选择子,所以,装入到LDTR的选择子必须确定一个位于GDT的位LDT类型的系统段描述符,也就是说,选择子中的TI位必须是0(前面提到过),而且这个描述符的类型字段(属于“段类型”的一部分)所表示的类型必须是LDT。 3.3.2.3 中断描述符表寄存器IDTR

中断描述符表寄存器IDTR指向中断描述符表IDT。如图3-5所示,IDTR长48位,其中32位的基地址规定IDT的基地址,16位的界限规定IDT的段界限。由于80386只支持256个中断+异常,所以IDT表的最大长度为2K。

IDTR指示IDT表的方式与GETR指示GDT表的方式相同。

任务状态段寄存器TR包含指示描述当前任务的任务状态段(TTS)的描述符选择子,

19

3.3.2.4 任务状态段寄存器TR

华北电力大学本科毕业设计(论文)

从而规定了当前任务的状态段,任务状态段的格式在后面的任务切换中详细介绍。

如图3-5所示,和LDTR一样,TR也有程序员可见和不可见部分(其实不可见部分都是之前讲到过的高速缓冲寄存器)。当把任务状态段的选择子装入到TR可见部分时,处理器自动把选择子所指向的描述符中的三个参数信息(段基址地址,段界限,段属性)保存到不可见的高速缓冲寄存器,之后对当前任务状态段的任何访问都可以方便地进行。

和LDTR类似,装入到TR的选择子必须不能为空,必须是指向GDT中的全局描述符,且描述符的类型必须是TSS(任务状态段)。

3.4 系统段描述符、门描述符、任务状态段

经过上一节的叙述,对分段管理机描述符制和支持分段管理机制的数据结构以及寄存器有所了解,但是仍然遗留有几个疑问,任务状态段TSS是什么?LDTR和TR中的选择子(程序员可见的部分)指向的都是GDT中的描述符,这两类描述符和普通的全局描述符有什么不同,各自有什么特点?怎样实现任务之间的切换?又需要什么数据结构或者什么寄存器?任务之间的切换的整个运行原理是怎样的?这是这一节和下一节需要叙述的内容。这一节的重点很集中,就是弄明白任务之间切换的一些基础知识,下一节详细讲述任务之间的切换,这是在80386的保护模式下设计和实现多任务操作系统的关键!!

3.4.1 系统段描述符

系统段是为了实现“存储管理机制”所使用的一种特别的段。在80386中,有两种系统段:“任务状态段TSS”和“局部描述符表LDT段”,用于描述系统段的描述符称为系统描述符,也成为特殊段描述符(这些描述符都在GDT中,这个上面已经详细讲过)。

系统段描述符的一般格式与普通存储段描述符的格式很相似,而区分的标志在于段属性中的DT位,DT = 1表示存储段,DT = 0表示系统段。系统段描述符和普通存储描述符在大多数字段(如段基地址和段界限)意义相同,需要注意的仅仅是段属性中的TYPE字段,其包含4位,其含义与存储段描述符的类型完全不同,详细见表3-1。

表 3-1 系统段描述符TYPE字段编码含义

类型编码 0 1 2 3 4 5 6 7 说明 未定义 可用286TSS LDT 忙的286TSS 286调用门 任务门 286中断门 286陷阱门 20

类型编码 8 9 A B C D E F 说明 未定义 可用386TSS 未定义 忙的386TSS 386调用 未定义 386中断门 386陷阱门 华北电力大学本科毕业设计(论文)

由表3-1可知,只有类型编号为1,2,3,9,B的描述符才是“系统段描述符”,其他类型的描述符都是门描述符,门描述符在下面会得到详解。

一个LDT段描述符用于描述一个任务的局部描述符表(长度不固定),也可以说是一个特殊段,LDT段描述符必须在全局描述符表GDT中才有效。在装载LDTR寄存器时,描述符中的LDT段基地址和段界限等信息被装入LDTR高速缓冲寄存器中。

一个任务状态段TSS(长度固定)用于保存任务的各种状态信息,而一个任务状态段描述符用于描述一个任务状态段TSS。在装载任务状态段寄存器TR时,描述符中的TSS段基地址和段界限等信息被装入TR高速缓冲寄存器中。在任务切换或执行LTR指令时(Load TR,顾名思义,装载TR寄存器),要装载TR寄存器。

利用段间转移指令JMP和段间调用指令CALL,直接通过TSS描述可实现任务切换。

3.4.2 门描述符

为了控制“任务内变换特权级”和“任务之间进行切换”,CPU一般通过“控制门”进行转移。

上一小节提到,系统段描述符的一般格式与普通存储段描述符相比,相当相似,而却分的标志在于段属性中的DT位,DT = 1表示存储段,DT = 0表示系统段。其实即使DT=0,也不完全是系统段描述符,只有当TYPE字段为1,2,3,9,B时,才是系统段描述符,如果TYPE字段为其他的话,这个描述符就是个“门描述符”。

“门描述符”与其他类型的段描述符有些不一样,系统段描述符和普通存储段描述符描述的都是一个段,通过描述符中的三个参数详细地定义和描述一个段,从而让CPU通过这样的存储机制迅速地找到内容。而“门描述符”并不描述某种内存段,而是描述控制转移的入口点,这种描述符好像一个通到另一段代码的“门”,通过这个门,可以实现“任务内特权级的变换”和“任务间的切换”。这种“门描述符”也被称作为“控制门”。

由于“门描述符”的特殊性,这里给出“门描述符”的数据结构: Gate struct ;门结构类型定义 OffsetLow DW 0 ;32位偏移的低16位 Selector DW 0 ;选择子 DoubleCount DB 0 ;双字计数字段 GateType DB 0 ;类型

OffsetHigh DW 0 ;32位偏移的低16位

而由表3-1可知,“门描述符”又可以分为:任务们、调用门、中断门、陷阱门。 “调用门”描述某个子程序的入口,“调用门”内的选择子必须指向代码段描述符,“调用门”内的偏移是对应代码段内的偏移。利用段间调用指令CALL,通过调用门可以实现任务内从外层特权级(低)到内层特权级(高)。

“任务门”指示任务。“任务门”内的选择子必须指向GDT中的任务状态段TSS描述符,

21

华北电力大学本科毕业设计(论文)

“任务门”中的偏移没有意义。任务的入口点保存在TSS中,利用段间转移指令JMP和段间调用指令CALL,通过任务门可以实现切换。

“中断门”和“陷阱门”分别描述中断和异常处理程序的入口点。“中断门”和“陷阱门”的选择子必须指向代码段描述符,门内的偏移就是对应代码段的入口点偏移。“中断门”和“陷阱门”只有在中断描述符表(IDT)里面才有效。

3.4.3 任务状态段TSS

每个任务有一个任务状态段TSS,用于保存任务的相关信息,在“任务内变换特权级”和“任务之间进行切换”时,要用到这些信息。

通过任务状态段的英文解释可以知道,任务状态段(Task State Segment)是保存一个任务重要信息的特殊段。任务状态段描述符用于描述这样的系统段。任务状态段寄存器TR的可见部分含有当前任务的任务状态段的段选择子,TR的不可见部分(高速缓冲寄存器)含有当前任务状态段的段基地址和段界限。

TSS在任务切换的过程当中起到了非常重要的作用,操作系统通过它实现任务的挂起和恢复。通俗点说,任务切换是指挂起当前正在执行的任务,恢复另一个任务的执行。在任务切换过程中,第一步,处理器中的各个寄存器的值被自动保存到TR所指定的TSS中,通俗来说就是保存现有任务的状态,第二步,下一个任务的TSS的选择子被装入TR,第三步,从TR所指定的TSS中取出各个寄存器的值送到处理器的各个寄存器中,通俗点说就是载入下一个任务的信息。

任务状态段TSS的基本格式如图3-7所示,由图可知,TSS的基本格式由104字节,这104字节的格式是不可改变的,但此外系统软件还可以指定若干附加信息。

图 3-7 TSS的基本格式

22

华北电力大学本科毕业设计(论文)

3.5 控制转移

控制转移可以分为两大类:“同一任务内的控制转移”和“任务间的控制转移”(任务切换)。“同一任务内的控制转移”可分为:“段内转移”(“段内转移”与实模式下的转移相似,不涉及特权级变换和任务切换)、“特权级不变的段间转移”、“特权级变化的段间转移”

3.5.1 任务内无特权级变换的转移

段内转移基本与实模式下相似,不涉及到特权级变化和任务切换,这里不过多说明。只有各种形式的段间转移才涉及到特权级变化和任务切换。 3.5.1.1 可以实现“段间转移”的几条指令

与实模式下一样,指令JMP、CALL和RET都具有段间转移的功能,指令INT和IRET总是段间转移(不存在段内转移),此外,中断和异常也会引起段间转移。

这些能够实现段间转移功能的指令被称作为“段间转移指令”。

在保护模式下,段间转移的目标位置由选择子和偏移地址构成,常把它称作是“目标地址指针”。

“段间转移指令JMP”和“段间调用指令CALL”还可以分为段间直接转移和段间间接转移两类:如果JMP和CALL指令中直接含有“目标地址指针”(上面提到的,由选择子和偏移地址构成),那么就是段间直接转移;如果指令中含有指向包含目标地址指针的门描述符或TSS描述符的指针,那么就是段间间接转移,这种指针只有选择子部分优先,这个选择子用来指示调用门、任务门或者TSS描述符,偏移部分没有意义。

实际上,可以简单地总结来说,当JMP和CALL指令所含指针的选择子部分指示的是“代码段描述符”(即普通的存储段,不是系统段描述符和门描述符),那么就是段间直接转移;当选择子部分指示的是“门描述符”或者属于“系统描述符”中的“TSS描述符”,则是段间转移。

3.5.1.2 向目标代码转移的步骤

处理器在执行上一小节说道的“段间转移指令”,从而向目标代码段实施转移的过程中,一般至少要经过如下步骤:

a) 判别目标地址指针内的选择子指示的描述符是否为空描述符。空描述符是GDT中

的第0个描述符,是一个特殊的描述符。目标代码段描述符不能是空描述符。 b) 从全局或者局部描述符表中读出“目标代码段描述符”。由选择子中的TI位确定

是从局部描述符表还是全局描述符表。

c) 根据具体情况,判断描述符类型是否争取,调整RPL。

d) 将目标代码段描述符内的有关内容装载到段寄存器CS的高速缓冲寄存器 e) 判别目标地址指针内的偏移是否超越代码段。目标地址指针内的偏移必须不超过

23

华北电力大学本科毕业设计(论文)

目标代码段界限

f) 装载CS段寄存器和指令指针寄存器EIP,CPL存入CS内选择子的RPL字段。 3.5.1.3 任务内无特权级变化的转移的几种实现方式

所谓“任务内无特权级变换的转移”是指:在转移到新的代码段时,当前特权级CPL利用“段间转移指令JMP”、“段间调用指令CALL”、“段间返回指令RET”和“INT指令”、a) 利用段间直接转移指令JMP或者CALL。在执行段间转移指令JMP时,如果指令内

所含指针指示一个代码段,那么就直接开始上一小节说明的向目标代码转移的步骤;在执行段间调用指令CALL时,如果指令内的指针指示一个代码段,那么就把返回地址指针压入到堆栈,然后直接开始上面说明的向目标代码转移的步骤。顺利通过这几步,就能完成任务内无特权变化的段间转移。

b) 利用段间返回指令RET。在执行段间返回指令RET时,如果从堆栈中弹出的目标地

址指针指示一个代码段,并且选择子符合RPL = CPL的条件,那么就开始上述的向目标代码转移的步骤。顺利通过这几步,就能完成任务内无特权变化的段间转移。通常情况下,段间返回指令RET和段间调用指令CALL对应出现。再利用段间调用指令CALL以任务内无特权级变换的方式转移到某个子程序之后,在子程序内利用段间返回指令RET以任务内无特权级变换的方式返回主程序。由于调用时无特权级变换,所以返回时也无特权级变化,所以必定满足RPL = CPL。 c) 利用调用门或者其他途径。利用调用门实现任务内如特权级变换的转移和其他途

径的转移在下一小节介绍。

保持不变。

“IRET”指令都可以实现任务内无特权级变化的转移。

3.5.2 任务内不同特权级的变换

在一个任务内,可以存在四种特权级,所以常常会发生不同特权级之间的变换。例如:外层的应用程序调用内层操作系统的例程,以获得必要的诸如内存分配等系统服务,而内层操作系统的例程完成以后,返回到外层应用程序。

在同一任务内,实现特权级从内层到外层变换的普通途径是:使用段间调用指令CALL,通过调用门进行转移;实现特权级从内层到外层变换的普通途径是:使用段间返回指令RET。值得注意的一点是,不能够利用JMP指令实现任务内不同特权级的变换。 3.5.2.1 通过调用门的转移

当段间转移指令JMP和段间调用指令CALL所含指针的选择子指示调用门描述符时,就可以通过调用门的转移。

值得注意的是,CALL指令在最后把目标代码段的指针装入CS和EIP之前,要把原CS和EIP(即返回地址)装入到堆栈。如果没有特权级的变化,堆栈保持不变,返回地址就保存在原堆栈中,如果变换特权级,那么返回地址保存在内层堆栈中。

24

华北电力大学本科毕业设计(论文)

3.5.2.2 堆栈切换

在使用CALL指令,通过调用门向内层转移时,不仅特权级发生变化,控制转移到一段新的代码段,而且也切换到内层的堆栈段。从任务状态段TSS的格式可以看出,TSS中包含有指向0级、1级、2级堆栈的指针。在特权级发生向内层变换时,根据特权级使用TSS中相应的堆栈指针对寄存区SS以及ESP进行初始化,建立一个空的内层堆栈。

在建立完了一个空的内层堆栈以后,先把外层堆栈的指针SS以及ESP寄存器的值压入内层堆栈,使得当控制转移向外层返回的时候可以恢复外层堆栈。然后从外层堆栈复制以双字为单位的调用参数到内层堆栈,调用门中的DoubleCount字段值决定了的数量。这些被复制的参数是主程序通过堆栈传递给子程序的实参,在调用之前被压入外层堆栈。通过复制堆栈中的参数,使内层的子程序不需要考虑堆栈的切换,而容易地访问主程序传递过来的实参。最后,调用的返回地址被压入堆栈,以便在调用结束的时候返回。

如图3-8所示,图中描述了在向内层变换时,建立内层堆栈,并从外层堆栈复制两个双字参数到内层堆栈的。图中每项是双字,可见的段寄存器内的选择子被扩展成32位,高16位为0。

要注意的是,无论是否通过调用门,只要是没有发生特权级变换,就不会发生堆栈的切换,切换前后使用一个堆栈。

3.5.2.3 向外层返回

与使用CALL指令通过调用门向内层变换相反,使用RET指令实现向外层返回。段间返回指令RET从堆栈中弹出返回地址,并且可以采用调整ESP的方法,跳过相应的在调用之前压入堆栈的参数(例如图3-8中,返回前,可以修改ESP的值,使之指向“外层ESP”,

25

图 3-8 堆栈切换

华北电力大学本科毕业设计(论文)

这样就跳过了“参数2”和“参数1”)。返回地址的选择子指示要返回的代码段描述符,从而确定返回的代码段。选择子的RPL确定返回后的特权级,而不是对应描述符的DPL,这是因为,段间返回指令RET可能使控制返回到一致代码段,而一致代码段可以在DPL规定的特权级以外的特权级执行。

3.5.3 任务间控制转移----“任务切换”

利用段间转移指令JMP或者段间调用指令CALL,通过任务门或者直接通过任务状态段TSS,都可以切换到别的任务。此外,在中断/异常或者执行IRET指令时也可能发生任务切换。

3.5.3.1 “直接通过TSS进行任务切换”和“直接通过任务门进行任务切换”

当段间转移指令JMP或段间调用指令CALL所含指针的选择子指示一个可用任务状态段TSS描述符时,正常情况下就发生从当前任务到由该TSS对应的目标任务的切换。目标任务的入口点由目标任务TSS内的CS和EIP字段所规定的的指针确定,而指令内部的偏移将被丢弃。

另一中进行任务切换的方式是,直接通过任务门进行任务切换。我们知道,任务门内的选择子指示某个任务的TSS描述符。当段间转移指令JMP或段间调用指令CALL所含指针的选择子指示一个任务门时,正常情况下就发生任务切换,也就是从当前任务切换到由任务门内的选择子所指示的TSS描述符对应的任务,也就是目标任务。此时,JMP或CALL指令内的偏移和任务门内的偏移都没有意义。 3.5.3.2 任务切换过程

根据指示目标任务TSS描述符的选择子进行任务切换的一般过程如下:

a) 测试目标任务状态段TSS的界限。TSS用于保存任务的各种状态信息,不同的任务,

TSS中可以由数量不等的其他信息,但根据任务状态段TSS的基本格式,TSS的界限应该大于或等于103。

b) 把寄存器现场保存到当前任务的TSS。把通用寄存器、段寄存器、EIP以及EFLAGS

的当前值保存到当前TSS中。保存的EIP的值是返回地址,指向引起任务切换指令的下一条指令,任务返回后继续执行。但是,LDTR和CR3不被保存入TSS中。 c) 把指示目标任务TSS的选择子装入TR。同时,把对应TSS描述符装入TR高速缓冲

寄存器中。此后,当前任务改称为原任务,目标任务改称为当前任务。 d) 基本恢复当前任务(目标任务)的寄存器现场。根据保存在TSS中的内容,恢复

各通用寄存器、段寄存器、EFLAGS以及EIP。在装入段寄存器的过程中,为了能够正确处理可能发生的异常,只把对应选择子装入各段寄存器。还要装载CR3寄存器。

e) 进行链接处理。

f) 把CR0中的TS标志置为1。将CR0中的TS标志置为1的目的是为了表示已经发生

26

华北电力大学本科毕业设计(论文)

过任务切换,在当前任务使用协处理器指令时,产生自陷。由自陷处理程序完成有关协处理器现场保存和恢复,这个有利于快速地切换任务。 g) 把TSS中的CS选择子的RPL作为当前任务特权级设置为CPL。

h) 装载LDTR寄存器。一个任务可以有自己的LDT,当然,也可以没有。当任务没有

LDT时,TSS中LDT选择子为0。如果TSS中LDT选择子非空,则从GDT中读出相应LDT描述符,在经过测试后,把所读LDT描述符装入LDTR高速缓冲寄存器。如果LDT选择子为空,则将LDT的存在位设置成0,标明任务不适用LDT。 i) 装载代码段寄存器CS、堆栈段寄存器SS和各个数据段寄存器以及其他的高速缓冲

寄存器。

j) 把调试寄存器DR7中的局部启用位设置为0,以清除局部于原任务的各个断电和方

式。

27

华北电力大学本科毕业设计(论文)

第4章 MyOS具体设计与实现

4.1 MyOS的开发语言、开发流程以及开发环境简述

由于多次需要提到本文所设计的操作系统,所以为这个操作系统取名MyOS。MyOS使用的开发语言为汇编语言和C语言。在编码的过程中,为了使代码逻辑清晰且易于维护,能用C语言的地方都避免使用汇编,利用汇编语言完成的任务有:在系统启动前期用汇编完成一些操作系统准备工作;以及贯穿整个操作系统运行时期,对某些C语言无法操作的寄存器进行的操作;其最重要的贡献还是完成了由16位实模式向32位保护模式的跳转,以及跳转之前利用16位实模式可以调用BIOS中断函数获取和设置一些关于硬件的信息,如果某些信息需要保存,则保存在某个约定的内存地址,以供运行于32位保护模式时期的操作系统使用。(32保护模式不能调用BIOS中断函数,因为BIOS中断函数都是16位代码段)

MyOS的开发流程并不复杂,主要按照如下几个步骤进行:

a) 在Windows系统上编写源代码,包括c语言源代码和汇编源代码,以及makefile

文件。

b) 用C语言编译器和汇编语言编译器编译源代码,并生成各自的机器语言文件。 c) 对机器语言进行链接,并用软件生成软盘映像文件。 d) 将映像文件写入磁盘(软盘),做成含操作系统的启动盘。 下面对这几个步骤涉及到的具体信息进行详细说明。

a) C编译器。由于现在使用较多的C编译器都是以开发Windows或者Linux上的应用

程序为前提而设计的,但是如果用来对编写操作系统的C源代码进行编译的话,可能会有一些不便的地方,所以此次毕设使用的是为开发操作系统而做出了些修改的类GCC编译器。

b) 汇编编译器。为了使汇编之后的机器代码能够与这个类GCC编译器编译出来的机

器代码保持一致,此次毕设使用的是和这个类GCC编译器配套的汇编编译器。 c) 软盘映像文件。软盘映像文件就是每一个bit都与实体软盘一一对应的文件,例

如,一个装满了文件的1440K的软盘,其软盘映像文件就是1440K,而且从头到尾,软盘映像中的每一位(“0”或者“1”)都和实体软盘相同。软盘映像文件可以用于模拟虚拟机的软盘,编写好的操作系统经过编译成机器代码以后,可以写入到映像文件中,用虚拟机以虚拟软盘位启动盘,可以得到和真机运行相同的效果。这样的一个工具大大地方便了系统的开发。在系统的开发过程中,与软盘映像文件对应的有两个软件,一个是软盘映像写入软件,其功能把映像文件当做是一个普通文件,将二进制文件写入到映像文件中,主要用于将编译好的系统写入到映像文件中;另一个软件是虚拟软盘存取软件,这个软件可以将软盘映像文件模拟

28

华北电力大学本科毕业设计(论文)

成一个机器上的硬盘驱动器,可以像操作硬盘的一个分区一样操作这个软盘,例如新建文件、打开文件、修改文件、保存文件、修改目录,不过支持的格式只有FAT12、FAT16、FAT32格式,本人没有这个软件的源代码,但是猜测这个软件的运行原理应该是:根据FAT12、FAT16、FAT32格式文件系统的运行原理,直接对文件系统的根目录区、FAT区、数据区进行模拟,例如:如需要新建一个文件,这个软件就会要在根目录区对应的文件位置新建一个文件名(当然,包括这个文件的诸多信息),接着如果向这个文件写入内容,这个软件就会向软盘映像文件中对应的位置写入这个文件的信息,同时更新FAT表。总结来说,就是如果在实体软盘中做的一些文件操作,你使用的系统怎样写入或读取实体软盘,这个软件就怎样对待软盘映像文件。

d) Makefile文件。到现在为止,需要要用到的软件有C编译器、汇编编译器、软盘

映像写入文件、虚拟软盘存取软件,当然还有一个链接器用来把所有的目标文件链接在一起,再写入到软盘映像文件中,而这些软件的使用都是命令行方式,如果每编译一个文件,都要输入一行命令,效率非常低,所以就使用脚本批处理文件,但是批处理也有其缺陷,因为将来MyOS的文件非常之多,而且随着开发的推进,文件之间有很多依赖关系,这样如果改动一个文件,就要多次运行批处理,而且必须牢记每个文件之间的关系。为了解决这个问题,引入了makefile文件,有了这个机制,每次只需要运行一个命令,就能完成所有的工作,当有新的依赖关系或者添加新的内容时,修改makefile文件即可。

系统的开发的环境。汇编用的是记事本,C语言用的是Visual C++ 6.0。

4.2 MyOS运行原理简述

4.2.1 引导扇区

计算机加电以后,BIOS程序引导计算机的启动,它首先检查被设置用来启动的磁盘(硬盘、软盘、闪存)中的第一个扇区(512字节),并检查这个扇区的最后两个字节,如果这两个字节是55 AA,则说明这个扇区被设置为启动扇区,这时BIOS会“指挥”CPU跳到这个扇区的某个地址(这个地址的信息在扇区存储),开始顺序执行,也就是将执行的权利交到这个启动扇区,而再后来这个启动扇区又会跳转到操作系统内核代码,这样,操作系统就运行起来。

引导扇区要做的第二件事情是从BIOS读出机器的信息,保存到内存的某个约定的地址,这样做的原因是:机器一开始运行在16位的实模式下,可以利用BIOS中断获得很多信息,进行很多操作,而到32保护模式下,就无法运行BIOS中的16位代码段,无法使用BIOS的中断函数。引导扇区的编写是用汇编,直接汇编成机器代码后写入到软盘映像文件的第一个扇区,当然,在写汇编代码的时候,必须控制汇编之后目标文件的大小为512字节,并且最后的两个字节是55 AA。

29

华北电力大学本科毕业设计(论文)

4.2.2 加载内核

上一节讲的是引导扇区引导计算机启动并保存机器的相关信息到内存。而引导扇区只 实际的操作系统在从引导扇区启动之后,都会读入并运行一个加载内核的Loader,有512字节大小,它必须尽快完成一些任务,之后把权利交出去。

这个Loader负责加载内核和模式的切换,然后把权利交给内核。而本次的MyOS系统为了简便,直接在引导扇区里加载内核,而模式的切换并入到系统的内核当中。

加载内核的过程很简单,就是利用BIOS中的读取磁盘中断服务程序,将之前写入磁盘的系统文件拷贝至内存,然后把CPU的EIP寄存器设置为系统所在的位置,这样系统就得到执行了。

4.2.3 执行系统内核

执行系统内核只是一个概念,不需要详细叙述。但是到此为止,还没有介绍MyOS具体信息,这里就先给出一个MyOS运行时的内存分布图,如图4-1,以后详细介绍系统各部分的时候,可以心中有数。图4-2是实模式下使用的1M内存空间,在保护模式下依旧使用,里面存有启动时期保存的有关机器的信息,在保护模式下需要使用。

图 4-2 保护模式和实模式都使用的内存中前1MB空间分布图

30

图 4-1 MyOS系统的内存分布图

华北电力大学本科毕业设计(论文)

4.3 MyOS内核设计与实现

4.3.1 内存管理

内存管理的主要内容是内存容量的检查、内存分配、内存释放,而为了方便管理大内存,减少磁盘外部碎片,还要设计一个以4KB为单位的内存分配和内存释放,这两个功能在整个系统中是内存管理的主要体现形式。 4.3.1.1 内存容量检查

内存管理的第一步是弄清楚内存的大小。而在机器最初启动的时候,BIOS已经检查了内存,并且会保存内存信息。但是问题是,BIOS版本不同,调用方法也不同,所以,通过别的手段得到内存的有效大小。

得到内存有效大小的原理是:往内存的一个单元写入一个值,再从这个单元读出这个但是有一点要注意,从486以后英特尔就在处理器中加入了一个高速缓存,利用局部值,如果相等,则说明该内存是可以使用的内存。

性原理提高CPU的吞吐率,缓冲CPU和内存之间的速度差别的矛盾。而如果高速缓存起作用的话,CPU读出和写入的第一个对象是高速缓存,尤其是在写入某个单元后再次读出这个单元的情况下,这个值并不会真正写入到内存,而是会在高速缓存中。所以,先要将CPU的高速缓存禁止,在测试完内存容量以后再次打开。

怎样禁止缓存呢?控制寄存器CR0中有一位标志位是控制缓存是否有效的,可以对它以下是内存大小检查的代码。

unsigned int memtest(unsigned int start, unsigned int end) {

/* 确认CPU是386还是486以上的 */ eflg = io_load_eflags();

eflg |= EFLAGS_AC_BIT; /* AC-bit = 1 */ io_store_eflags(eflg); eflg = io_load_eflags();

if ((eflg & EFLAGS_AC_BIT) != 0) { /* 如果是386,即使设定AC = 1,AC的char flg486 = 0;

unsigned int eflg, cr0, i;

进行操作。

值还是会自动回到0 */

}

eflg &= ~EFLAGS_AC_BIT; /* AC-bit = 0 */

31

flg486 = 1;

华北电力大学本科毕业设计(论文)

}

io_store_eflags(eflg);

if (flg486 != 0) { }

i = memtest_sub(start, end);//这个函数使用汇编写的,返回能用的内存的最大地址 if (flg486 != 0) { }

return i;

cr0 = load_cr0();

cr0 &= ~CR0_CACHE_DISABLE; /* 允许缓存 */ store_cr0(cr0); cr0 = load_cr0();

cr0 |= CR0_CACHE_DISABLE; /* 禁止缓存 */ store_cr0(cr0);

4.3.1.2 用于内存管理的数据结构设计

在操作系统的课程上,主要介绍了两种内存管理的方式,一种是位示图法,即用1位来表示一个单位的内存是否正在使用。例如,128M的内存,共有0x08000000个字节,以0x1000(4KB)为单位进行管理,需要创建0x08000000/0x1000 = 32768个单位进行管理。还有一种是空闲分区链管理法,即记录下每一块空闲区域的起始地址和大小,每当有内存分配时,则遍历所有空闲区域的记录,找到一块合适的分配给它,然后再更新内存的空闲区域记录;释放时也一样,更新相应的空闲区域记录,但是有一点要注意,内存释放的空间上面和下面的空间也可能是空闲的,这样就要与上面和下面空闲的空间合并,在空闲区域记录中归纳成一条,要不然系统运行到后面就没有大块的内存可用,全部都被分割成小块的空闲区域。

MyOS中采用的是第二种管理方法,其原因为:首先占用内存少,而且大块内存分配和以下是用于内存管理的数据结构设计。

#define MEMMAN_FREES

4090 /* 最大个数32KB */

#define MEMMAN_ADDR 0x003c0000 /* 约定的存放内存管理信息(结构体信息)的首地址*/

struct FREEINFO { };

struct MEMMAN {

/* 内存管理器 */

32

释放时都特别迅速,如果用位示图法,需要对大量的内存进行读写。

/* 一块空闲区域 */

unsigned int addr, size;

华北电力大学本科毕业设计(论文)

int frees; /* 空闲内存区域的数量 */

int maxfrees; /* 最大的空闲内存区域大小 */ };

struct FREEINFO free[MEMMAN_FREES]; /* 所有的内存区域记录 */

4.3.1.3 内存分配

MyOS的内存分配使用的是最大内存分配法,即从多个内存空闲区域中找到最大的区域,以下是用于内存分配的函数。

unsigned int memman_alloc(struct MEMMAN *man, unsigned int size) /* 分配内存空间 */ { }

unsigned int i, a;

for (i = 0; i < man->frees; i++) { }

return 0; /* 无可用空间 */

if (man->free[i].size >= size) { }

/* 找到足够大的内存 */ a = man->free[i].addr; man->free[i].addr += size; man->free[i].size -= size; if (man->free[i].size == 0) { }

return a;

/* 如果free[0]变成了0,就减少一条可用信息 */ man->frees--;

for (; i < man->frees; i++) { }

man->free[i] = man->free[i + 1]; /* 后面整体后移 */

分配给申请者所申请内存大小之后更新这个空闲内存区域的大小。

4.3.1.4 内存释放

内存释放时要注意的是,每次释放一块内存,必须检查这块内存上面和下面是否同样以下适用于内存释放的函数。

int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size)

33

是空闲的内存,如果是,则需要合并,更新相应的内存区域信息。

华北电力大学本科毕业设计(论文)

/* 释放内存空间 */ {

int i, j;

/* 为了方便整理释放后的内存空间,将free[]按照地址顺序排列 */ for (i = 0; i < man->frees; i++) { }

/* free[i - 1].addr < addr < free[i].addr */ if (i > 0) { }

/* 不能够与前面的空间结合在一起 */ if (i < man->frees) {

/* 后面也有 */

if (addr + size == man->free[i].addr) {

/* 可以与后面的内容归纳在一起整理 */

34

if (man->free[i].addr > addr) { }

break;

}

if (man->free[i - 1].addr + man->free[i - 1].size == addr) { /* 当前释放的内存前面也有空闲的内存 */ man->free[i - 1].size += size; if (i < man->frees) { }

return 0;

/* 后面也有 */

if (addr + size == man->free[i].addr) { /* 也可以与后面的可用内存归纳在一起 */ }

man->free[i - 1].size += man->free[i].size; /* man->free[i]删除 */

/* free[i]变成0后归纳到前面去 */ man->frees--;

for (; i < man->frees; i++) { }

man->free[i] = man->free[i + 1]; /* 整体挪动一个位置 */

华北电力大学本科毕业设计(论文)

}

}

}

man->free[i].addr = addr; man->free[i].size += size; return 0;/* 成功 */

/* 前面后面都没有空余的空间,单独处理 */ if (man->frees < MEMMAN_FREES) { }

/* 不能往后移动 */ man->losts++;

man->lostsize += size; return -1; /* 失败 */

/* free[i]之后的整体完后挪动一个位置 */ for (j = man->frees; j > i; j--) { }

man->frees++;

if (man->maxfrees < man->frees) { }

man->free[i].addr = addr; man->free[i].size = size; return 0; /* 成功 */

man->maxfrees = man->frees; /* 更新最大值 */ man->free[j] = man->free[j - 1];

4.3.1.5 以4K为单位分配和释放内存

如果总是以1个字节为单位分配内存,会产生很多外部碎片,而内存控制器的空闲区域记录个数是有限的,所以为了防止空闲区域记录个数被耗尽,需要修改分配和释放函数,以4K为单位分配和释放内存。

以下是以4K为单位分配和释放内存的函数。

unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size) /*以4K为单位分配内存*/ {

unsigned int a;

size = (size + 0xfff) & 0xfffff000; a = memman_alloc(man, size);

35

华北电力大学本科毕业设计(论文)

}

return a;

int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size) /*以4K为单位释放内存*/ { }

int i;

size = (size + 0xfff) & 0xfffff000; i = memman_free(man, addr, size); return i;

4.3.1.6 其他与内存操作有关的函数

unsigned int memman_total(struct MEMMAN *man) /* 统计所剩内存的大小 */ { }

unsigned int i, t = 0;

for (i = 0; i < man->frees; i++) {

t += man->free[i].size;}

return t;

4.3.2 定时器设计

定时器对于操作系统有非凡的意义,简单点来说,定时器的工作原理就是:隔一段时间就发送一个中断信号给CPU。而如果有了定时器,CPU就不用亲自去计算时间。

定时器的操作者是和处理器集成在一起的8254芯片,可以通过CPU对8254芯片进行编程(通俗点说就是用户写指令,让CPU去执行,执行内容是对8254芯片的各种寄存器进行写入操作,执行结果8254芯片按我们想要的工作模式来工作)。 4.3.2.1 设置定时器芯片

8254芯片的官方定义是PIT(“Programmable Interval Timer”,可编程的间隔型定时器)。可以通过设置这个PIT,让定时器每隔多少秒就产生一次中断,而产生的中断怎样传输到CPU呢?PIT是和中断控制器(后面的“基本输入输出管理”中会详细介绍)的IRQ的0号相连接,所以,只要设定了PIT就可以设定IRQ0的中断时间,这样,CPU只需要关注IRQ0是否产生中断就可以了

通过对8254芯片的资料可以知道,控制其中断周期变更的命令是:OUT(0x34,AL),其中AL = 0x34;OUT(0x40,AL),其中 AL = 中断周期的低8位 ; OUT(0x40,AL),其中AL = 中断周期的高8位。

36

本文来源:https://www.bwwdw.com/article/1483.html

Top