3.1操作系统体系结构

该操作系统采用整体式体系结构。在整体式体系结构中,整个操作系统是由一堆过程的集合组成的,每个过程都可以调用任意其他过程。使用这种技术时,系统中的每一过程都有一个定义完好的接口,即它的入口参数和返回值,而且相互间的调用不受约束。

在整体式系统中,为了构造最终的目标操作系统程序,开发人员首先将一些独立的过程进行编译,然后用链接程序将其链接在一起成为一个单独的目标程序。从信息隐藏的观点看,它没有任何程度的隐藏-每个过程都对其他过程可见。(与此相对的是将系统分成若干个模块,信息被隐藏在这些模块内部,在外部只允许从预先定好的调用点访问这些模块)。

但即使在整体式系统中,也存在一些程度很低的结构化。操作系统提供的服务(系统调用)的调用过程是这样的:先将参数放入预先确定的寄存器或堆栈中,然后执行一条特殊的陷入指令,即访管指令或核心调用(kernel call)指令。

这条指令将机器由用户态切换到核心态,并将控制转到操作系统。该过程如图2-1所示。(多数CPU有两种状态:核心态:供操作系统使用,该状态下可以执行机器的所有指令;用户态:供用户程序用,该状态下I/O操作和某些其他操作不能执行。)

第三章 FOS操作系统设计-LMLPHP

图 3-1整体式操作系统的简单结构模型

 

图 3-1 系统调用的操作过程:(1)用户程序陷入核心 (2)操作系统确定所请求的服务编号 (3)操作系统调用服务过程 (4)控制返回到用户程序

这种组织方式提出了操作系统的一种基本结构:

l  一个用来调用被请求服务例程的主程序。

l  一套执行系统调用的服务例程。

l  一套支持服务例程的实用过程。


在这种模型中,每一条系统调用都由一个服务例程完成;一组实用过程用来完成若干服务例程都需要用到的功能,如从用户程序获取数据等,这种将各种过程分为三层的模型如图3-2所示。

第三章 FOS操作系统设计-LMLPHP

图3-2 系统调用的三层模型

3.2 FOS进程设计

3.2.1 FOS的中断处理

中断硬件的细节与系统相关,但任何系统都必须具有与Intel - 32位CPU功能等价的部件。由硬件设备产生的中断是一些电信号,它们首先由中断控制器进行处理。中断控制器是一片集成电路,它能够检测到许多这类电信号并在处理器的数据总线上为其生成唯一的数据格式。这些数据格式之所以必须唯一是因为:对所有这些设备,处理器本身只能有一个输入,所以它无法辨认需要服务的具体设备。使用32位处理器的PC机通常有两片中断控制器芯片,其中每一个可以处理8个输入,但其中有一片为从片,它的输出线连到主片的一条输入线,这样一共可以挂接15个不同的外部设备,这如图3-3中所示。

该图中,中断信号出现在右侧的IRQn信号线上。连到CPU INT管脚的连接线通知CPU发生了中断。从CPU发出的INTA(中断应答)信号使负责中断的控制器芯片将数据放在系统数据总线上并通知处理器应执行哪个服务例程。在系统初始化期间,当cmain调用init_irq()时,对中断控制器进行编程。这种编程内容决定了对应于各条输入线的信号将向CPU送出什么样的数据,同时也决定了中断控制器操作所用的其他参数。放在总线上的数据是一个8位(二进制)的数值,它用作对一个表格的索引,该表格最多可包含256项。FOS的表格中含有256个表项。在32位的Intel处理器上,这张表中包含中断门描述符,每个中断门描述符是一个含有若干域的8字节结构。

第三章 FOS操作系统设计-LMLPHP

图 3-3 中断硬件模型

 

对中断有几种可能的响应方式,在FOS使用的一种模式中,中断门描述符中最重要的一个域指向服务例程可执行代码段和其中的起始地址。CPU执行被选中的描述符所指向的代码。其结果与执行如下的汇编语言指令完全相同:

   int 

唯一的差别在于,对于硬件中断,来自中断控制器芯片的一个寄存器,而不是程序内存中的一条指令。

为了保证各个进程之间的内核堆栈不会互相干扰,我们给每个进程分配了一个独立的堆栈。当CPU在一个进程运行期间接收到一个中断时,它将建立一个新堆栈供中断服务程序使用。该堆栈的位置由任务状态段(Task State Segment - TSS)中的一项决定(每个进程均有一个任务状态段)。在FOS操作系统中,堆栈在虚拟内存的基址是0x0,栈顶是0x40000000。但是在实际的内存中,内核会调用内存管理模块为内核堆栈分配一个独立的内核堆栈。特别要注意的是,中断有四种情况:(a)是没有变换特权级和没有出错码的情形;(b)是没有变换特权级有出错码的情形;(c)是变换特权级和没有出错码的内层堆栈的情形。(d)是变换特权级和有出错码的内层堆栈情形。如图3-4

由图3-4可知外层SS,外层ESPEFLAGS,CS,EIP和出错码是CPU自动保存的当中断处理例程代码开始运行时,它将返回被中断进程所需的大部分信息保存在内核堆栈中,然后进行中断处理。

中断服务例程的结束操作如下:显式地弹出非硬件压栈的寄存器值,并执行一条iretd(从中断返回)指令。iretd恢复中断前的状态,恢复被硬件压栈的寄存器,并切换回中断前使用的堆栈。由此可知,中断停止一个进程,而中断服务结束则重启动一个进程。被启动的进程可能不是最近被停止的那个进程。与一般的汇编语言教材中所讲述的较简单的中断机制不同,在中断期间被中断进程的工作堆栈上并不保存任何内容。进一步而言,由于在中断之后堆栈被重新创建于一个已知的位置(由TSS决定),所以多进程的控制得以简化。启动另一个进程只需要通过jmp指令跳到一个TSS描述符即可。

第三章 FOS操作系统设计-LMLPHP

3-4  四种中断情况下的的堆栈操作

 

当接收到一个中断时,CPU关掉所有的中断。这个过程是自动进行的,但也存在可以关中断和开中断的汇编指令。中断处理例程在切换到位于进程表之外的核心栈之后重新开中断,当然在它切换回进程表中的堆栈之后必须再次关中断。但当它在处理中断时,其他中断可以发生并被处理。CPU跟踪下这些嵌套的中断,并在中断嵌套时使用一种更简单的方法来完成中断服务例程与被中断处理例程之间的来回切换。当正在执行一个中断处理例程(或其他核心代码)时,若接收到新的中断,则并不建立一个新堆栈,CPU把恢复被中断代码所需要的主要寄存器值压入已存在的堆栈。当执行核心代码时若遇到iretd指令,则同样也使用简化的返回机制。作为iretd动作的一部分,处理器通过检查从堆栈弹出的代码段选择符来决定如何处理iretd指令。

3.2.2 FOS进程空间分配

在FOS操作系统中,每个进程占有4G的虚拟空间,其中,0~0x3FFFFFFF共1GB为虚拟系统空间,0x40000000~0xFFFFFFFF共3GB为虚拟用户空间。如图3-5

第三章 FOS操作系统设计-LMLPHP

3-5 进程虚拟空间分布

3.2.3 FOS的进程调度

在多进程的操作系统中,进程是一个全局性的和关键性的问题,它对系统的总体设计,系统的实现,功能设置以及各方面的性能都有着决定性的影响。

但是,在FOS操作系统中,我们的目标仅仅是多进程技术,而非算法的效率,因此,在调度算法方面,我们设计得非常的简单。FOS使用的是一种基于信号量的优先级算法,每个进程处理一定的调度信号量,当必须运行新任务时,就会选择信号量最大的进程。时钟中断发生时,当前正在运行的进程就会减少一个信号量。当它的信号量变成零时,系统将此进程悬挂并且选择其他的进程。如果可运行的进程的信号量都为零,FOS将重新设定,增加系统中每个进程的信号量,而不仅仅是针对可运行的进程。(增加的方法)根据下面的公式:

信号量 = 优先级

即信号量取决于进程的优先级。

下面是调度程序和核心代码:
第三章 FOS操作系统设计-LMLPHP

首先寻找信号量最大的可运行进程,如果找到则跳转到该进程(通过switch_to(next)实现)。如果没有找到,即可运行的进程的信号量为0,则重新赋值每个可运行进程的信号量。

FOS操作系统中,时间片为10ms,所有进程的调度都应该通过schedule()函数。图3-6FOS进程状态转换图。

假如当前进程要进入某个高速缓冲块的等待队列,而且该等待队列上已经有另外两个进程task1task2先后进入。形成的队列如图2-8。等待队列是堆栈式的,先进入队列的进程排在最后(内核是通过函数的嵌套实现堆栈的)
第三章 FOS操作系统设计-LMLPHP

3-6 FOS进程状态转换图

3.3 FOS虚拟存储器设计

在一个系统中,内存往往是最紧张的资源,为了将这种资源合理的利用,就必须搞清楚内存是如何被使用的,只有这样才能进行合理而且高效的分配。进程有时需要读内存,有时需要写内存,这个过程似乎是随机的。但是对于每一个进程来说,有一个段是不会被写的,那就是代码段。这个段的内容由进程的可执行文件决定,不会改变,进程运行时,也不能修改这个段的内容。如果两个进程使用的是同一个可执行文件,比如:打开两个try.exe 文件(假定该可执行文件不支持多线程),这时如果让两个进程在内存中分别使用两份代码,将造成不必要的内存浪费,所以这种情况下,内核会让这两个进程使用同一个代码段的内存空间。

其实数据段也是可以共享的,同样是使用同一可执行文件的多个进程,他们在进程还没有开始执行时,数据段是相同的,随着进程的运行,数据段的内容可能会被改变。内存的访问具有局部性,在一段时间内可能不会有对数据段的某些区间修改,这个时间段可能很短,如果进程的数据操作完全靠堆栈来实现,这个时间段就可能是进程的整个生命周期。但是如何预测进程的数据操作是在哪里,如何预测数据段哪些区间可以共享,哪些不行,从而安排内存的使用?答案是否定的,对于现代操作系统而言, 这种预测是不现实的,或者代价相当大。与其花费大量精力去做预测,为什么不采用以逸待劳的办法呢?先将空间共享,等到进程对共享空间进行写操作时再取消对该页的共享。liunx 采用了一种称为写时复制(copy on write) 的机制。这种机制必须要有硬件的支持,在386 页面映射机制中,有一个读写权限标志位XW,将这一位设为只读(0)方式之后,如果进程试图对该页进行写操作,cpu 将出现页面异常中断,调用内核设定的页面异常中断处理程序,在这里内核将原来的页面复制一份,再取消对该页面的共享,这样就互不干扰了。有了这个保障,内核在进行内存共享操作时就可以放心了。

cpu 在进行内存访问时,可能因为缺页或者试图对一个只读页面进行写操作而产生页面异常,cpu进入相应的页面异常中断处理程序。由于异常可能由缺页或者写只读页面产生,两种情况的处理也是不同的,所以中断处理程序首先应该区分产生本次异常的原因,进入不同的处理过程。算法如下:


算法:page_fault

输入:出错码error_code

出错线性地址address

输出:无

{

保存现场;

根据出错码判断出错原因;

if(缺页)

     做缺页处理do_no_page(error_code, address);

else

     做写保护处理do_wp_page(error_code, address);

恢复现场;

return

}

 
 


 

 

 

 

 

 

 

 

 

 

 

 

 

 


x86 处理器中error_codecpu 产生并在保存了中断点的相关内容之后将其压入堆栈,出错码的最低位指示出错原因(1:写出错;0:缺页)。address 则是由一个专门的32 位寄存器cr2 保存。

在对进行进程初始设置时,内核并不是将进程可能用到的所有内存一次性分配给进程,而是在进程要访问该地址时分配,将内存分配给一定会被访问的空间,这样就提高内存资源的使用率。这样作就不可避免地会出现缺页中断。当cpu 访问一个内存单元时,如果该单元所在的页面不在内存中,cpu 将产生页面异常,进一步进入缺页处理程序,算法如下:

第三章 FOS操作系统设计-LMLPHP


       由于进程的fork share_page 操作,会出现多个进程共享一个物理页面的情况,这个物理页面被置为只读方式,如果其中一个进程想对这个页面进行写操作,cpu 就会产生页面异常中断,并进一步进入写保护出错处理。

在写保护出错处理中,将会根据情况复制被共享的页或者取消对页面的写保护,算法如下:

算法:do_wp_page

输入:出错码error_code

出错线性地址address

输出:无

{

if ( 出错页面属于主内存块且共享计数为1)

{

取消写保护;

刷新页变换高速缓冲;

return

}

申请一个新的物理页;

if ( 出错页面属于主内存块)

共享计数减1

使出错时的页表项指向新的物理页;

复制共享页的内容到新的物理页;

return

}


3.4 FOS I/O设计

3.4.1键盘设计

在键盘中存在一枚叫做键盘解码器(Keyboard Encoder)的芯片,它通常是Intel 8048以及兼容芯片,作用是监视键盘的输入,并把适当的数据传送给计算机。另外,在计算机主板上还有一个键盘控制器(Keyboard Controller),用来接收和解码来自键盘的数据,并与8259A以及软件进行通讯。当8048检测到一个键的动作后,会把相应的扫描码发送给80428042会把它转换成相应的Scan code set 1扫描码,并将其放置在输入缓冲区中,然后8042发送一个信号给8259A8259A产生中断(IRQ1)。如果此时键盘又有新的键被按下,8042将不再接收,一直到缓冲区被清空。

FOS操作系统在处理键盘输入扫描码的时候,为其建立了一个32个字节的缓冲区,以环的结构存储数据。在存储过程中,如果缓冲区已满,则直接丢弃收到的字节。

3.4.2屏幕控制

PC BIOS内存划分中,0xB8000~0xC0000是作为文本视频缓冲区用的。FOS操作系统将0xB8000~0xC0000总共32K的内存划分成8块,即每块4K,因此共有8个的屏幕可供切换。

3.4.3软盘驱动设计

PC机中,软盘控制器一般采用与NEC PD765Intel 8287A兼容的芯片,例如Intel82078。由于软盘的驱动程序比较复杂,因此下面对这类芯片构成的软盘控制器的编程方法进行较为详细的介绍。

典型的磁盘操作不仅仅包括发送命令和等待控制器返回结果,的软盘驱动器的控制是一种低级操作,它需要程序在不同阶段对其执行状况进行干涉。

在上述磁盘操作命令或参数发送到软盘控制器之前,必须首先查询控制器的主状态寄存器(MSR),以获知驱动器的就绪状态和数据传输方向。软盘驱动程序中使用了一个output_byte(byte)函数来专门实现该操作。该函数的等效框图如3-7

FOS操作系统是使用DMA控制方式处理中断的。DMA控制器的主要功能是通过让外部设备直接与内存传输数据来增强系统的性能。通常他由机器上的INTEL 8237芯片及其兼容芯片实现。通过对DMA控制器进行编程,外设与内存之间的数据传输能在不受CPU控制的条件下进行。因此在数据传输期间,CPU可以做其他事。DMA控制器传输数据的工作过程如下:

(1)初始化DMA控制器

(2)数据传输

(3)传输结束

使用DMA控制器时,通常需要按照一定的步骤来进行:

(1)关中断,以排除任何干扰

(2)修改屏蔽寄存器,以屏蔽需要使用的DMA通道

(3)向0X0C端口写操作,置字节先后触发器为默认字节

(4)写方式寄存器,以设置指定通道的操作方式字

(5)写地址寄存器,设置DMA使用的内存页面中的偏移地址

(6)写页面寄存器,设置DMA使用的内存页面

(7)写计熟寄存器,设置DMA传输的字节数

(8)再次修改屏蔽寄存器,以开启DMA通道

(9)最后,开启中断,以允许软盘控制器在传输结束后想系统发出中断请求
第三章 FOS操作系统设计-LMLPHP

3-7 output_byte(byte)函数等效框图

 

数据读或写操作需要分几步来完成。首先驱动器马达需要开启,并把磁头定位到正确的磁道上,然后初始化DMA控制器,最后发送数据读或写命令。另外,还需要定出发生错误时的处理方案。典型的操作流程图见3-8所示

第三章 FOS操作系统设计-LMLPHP

3-8  软盘数据读写流程图

FOS操作系统中,软盘一旦开启,就不再关闭,因此,实际上就省去了软盘开启的工作。这样做,简化了软盘驱动程序的编程。重新校正命令让磁头移动到零磁道,而磁头寻道命令则让磁头移动到指定的磁道上。这两个磁头定位命令与典型的读/写命令不同,因为它们没有结果阶段。一旦发出这两个命令之一,控制器将立刻会在主状态寄存器(MSR)返回就绪状态,并以后台形式执行磁头定位操作。当定位操作完成后,控制器就会产生中断以请求服务。此时就应该发送一个“检测中断状态”命令,以结束中断和读取定位操作后的状态。由于驱动器和马达启动信号是直接由数字输出寄存器(DOR)控制的,因此,如果驱动器或马达还没有启动,那么写DOR的操作必须在发出定位命令之前进行。流程图见图3-9所示。
第三章 FOS操作系统设计-LMLPHP

3-9 DOR流程图

3.5 文件系统设计

FOS操作系统采用的是FAT12文件系统的简化版,因为它的外设只有软盘,而且可以与现有的操作系统兼容。

FAT12DOS时代就开始使用的文件系统,直到现在仍然在软盘上使用。在软盘的第0个扇区是引导扇区。紧接着引导扇区的是两个完全相同的FAT表,每个FAT占用9个扇区。第二个FAT之后是根目录山区的第一个扇区。根目录扇区的后面是数据区。如图3-10

 第三章 FOS操作系统设计-LMLPHP

3-10 FAT文件系统的软盘分区

 

FAT区中,每个FAT项占用12位,包含一个字节和另一个字节的一半。

通常,FAT项的值代表的是文件的下一个簇号,但如果值大于或等于0xFF8,则表示当前簇是本文件的最后一个簇。如果值为0xFF7,表示它是一个坏簇。

根目录区的每一个条目占用32字节,它的格式如表3-11

 

名称

偏移(字节)

长度(字节数)

描述

DIR_Name

0

0xB

文件名8字节,扩展名3字节

DIR_Attr

0xB

1

文件属性

保留位

0xC

10

保留位

DIR_WrtTime

0x16

2

最后一次写入时间

DIR_WrtDate

0x18

2

最后一次写入日期

DIR_FstClus

0x1A

2

此条目对应的开始簇号

DIR_FileSize

0x1C

4

文件大小

3-10 根目录数据结构

 

FOS操作系统在软盘上的存储结构就是基于以上的算法实现的。

当文件加载进入内存是,FOS操作系统规定每个文件最大可分配8个页面(每个页面4K),共32K的存储空间。

由于对文件进行读写的时候,要注意到临界区问题,但有为了提高效率,因此,我们对每个页面都进行单独控制,这样,当一个页面进行修改的时候,并不妨碍其他用户访问那些不需要进行更改的页面。

 

 

 

 

09-29 18:24