第六章OSKit的应用实例:一个简单系统的设计与实现
6.1设计目的
OSKit是一个用来帮助我们研发操作系统的工具,如果你想更好的掌握它,除了对其本身进行分析之外,还有一个更加实际的方法,那就是利用这个工具开发一个我们自己的操作系统。
我们开发这个系统的初衷主要是为了学习,因此并没有想把它设计成像Windows、UNIX那么复杂。我们的目标是很明确的,那就是在大致搞明白OSKit应当如何使用之后,做一次尝试,用较短的时间,设计出一个具有一些最基本功能的系统。这对于我们课题组的每名成员来说,都意味着挑战。
6.2系统的功能
这个系统支持些什么?这是在开始设计之前我们要首先确定的一个问题。下面列出了一些我们希望实现的功能。
通过Multiboot或者LILO进行系统引导:这个系统可以通过所有支持Multiboot的引导程序进行引导,或者通过引导Linux的LILO进行引导。引导方式可以根据用户的要求,选择是通过软盘还是硬盘。
多线程:用户可以通过我们提供的系统调用,创建或杀死自己的线程。由于并不支持文件系统,因此新的线程恐怕只能是一个函数了。从理论上讲,我们可以让这个系统支持很多线程,但因为这仅仅是一个简单的演示性系统,因此我们把线程数限制在32个,当然这个数字也可根据用户的需求任意更改。
进程间通信:这个系统可以支持一些简单的进程间通信机制,比如信号量、消息队列等等。
外设:在众多的外设之中,我们选择了应用范围比较广的串口。利用目前OSKit所提供的机制,支持更多的设备是不成问题的,但加在这个系统中意义并不十分的大。另外,需要强调的是,访问串口的方法,是由我们自己规定,不允许用户更改。
简单的内存管理:应用程序需要的内存可以通过malloc函数动态申请。
总体构想
在该操作系统中,我们规定用户只能创建用户级线程,所有对于核心的操作都将由系统来完成。线程采用RR调度算法,也就是时间片轮转法,并允许抢占,线程在该系统中有四种状态,它们是运行态、阻塞态、睡眠态和就绪态,用户可以设定和改变线程的优先级。在线程中用户可以动态申请内存,但是需要由用户来释放。线程间提供简单的通信机制,包括信号量和消息队列。
在设备方面,对于终端,本系统将提供简单的输入输出功能。而对于串口的功能,本系统可以完整的实现,也就是说,用户既可以从串口读也可以向串口写。
为了方便起见,我们为这个系统起一个很简单的名字,叫acos,这个名字并没有什么实际的意义,仅仅为了叫起来方便。
虽然OSKit已经为我们提供了很多设计操作系统所必须的功能,比如内存分配中的malloc函数,通过Multiboot引导等等。然而,仍有许多工作是要由我们自己完成。
6.3我们自己所完成的工作:
6.3.1系统的启动
这里我们所说的系统启动,并不是指通常人们所说的通过软盘或硬盘引导,而是在引导结束之后,操作系统的核心进程如何启动第一个用户进程。通常,在UNIX系统中,内核启动之后,内核程序会去执行文件系统上的init程序,这个程序是整个操作系统中的第一个用户进程,剩下的进程,都必须通过这个进程才能启动。在acos中,我们规定,用户必须要提供一个acos_init( ) 函数,系统在启动初始化工作全部完成之后,acos_init( )函数将被作为系统中的第一个进程启动。这之后的工作,就完全由用户来完成了。
当进程acos_init( ) 结束时,整个系统也将终止运行。
在acos_init( )启动之前,究竟还有哪些事情需要做呢?下面我们就列出要做的事情:
对整个系统进行初始化,注册设备,检测设备,初始化设备需要的数据结构,初始化线程库,创建线程需要的数据结构。
6.3.2线程管理
所谓操作系统的线程管理,一般包括了一下的三大部分:线程的创建,线程间通信和线程调度。
线程的创建包括了初始化线程和创建线程的属性。
线程间通信中,我们使用了信号量,条件变量。
在线程调度中,用户线程只能使用时间片轮转法作为调度算法,并且规定在调度过程中调度算法不允许更改。
6.3.3 外设(串口)
在UNIX系统中,所有的设备都可以通过文件系统进行访问,读、写设备就像读写文件一样。在我们设计的这个acos中,并没有对文件系统提供支持,也不提供像读写文件系统一样的方法来访问设备。但是,我们提供一套基本的函数,供用户线程对串口进行读写操作,这些函数专门用于处理串口。
一个设备在同一时刻只能被一个线程使用,因此,在使用设备以前要申请,只有申请成功的线程才能够使用设备。
6.3.4 应用程序示例
为了能让各位老师对我们的演示系统一目了然,我们特地选取了在操作系统中比较有代表性的4个例子供大家欣赏,它们是,哲学家算法,时钟演示,线程调度和中断响应。
所需的资源:演示前三个例子需要一台intel 386或更新的计算机,一张1.44MB的软盘;演示第四个例子除了上述设备之外,还需要一根串口线,一台装有Windows 9x的计算机作发送机,并在其上安装有能向串口发送信息的软件。
1.哲学家算法
哲学家算法也称哲学家进餐的同步问题,最早是在1965年由Dijkstra提出并解决的。下面我就简单描述一下这个经典IPC问题的大意:7个哲学家围坐在一张圆桌周围,每个哲学家面前有一个碟通心面,由于面条很滑,所以要用两把叉子才能夹住。相邻两个碟子之间有一把叉子。哲学家的生活包括三种状态:即吃饭,思考和挨饿,当一个哲学家觉得饿时,他试图分两次去取他左边和右边的叉子,每次拿一把,但不分次序。如果成功地获得了两把叉子,他就开始吃饭,吃完以后放下叉子继续思考。这里的问题是:为每一个哲学家写一段程序来描述起行为,要求不能死锁。
在这个例子中我们设定了7个哲学家,且不允许哲学家饿死。我们为每个哲学家创建一个线程,把叉子看作条件变量,让线程等待条件变量,如果满足条件,则线程执行(哲学家的吃饭状态),执行一定时间之后,释放条件变量,进入休眠状态(哲学家的思考状态),当休眠时间过去之后,线程被唤醒,重新等待条件变量(哲学家的挨饿状态)。
2.时钟演示
计算机是以严格精确的时间进行数值运算和数据处理的,最基本的时间单元是时钟周期,OSKit最小可以精确到纳秒一级,其时钟系统的特点是允许在系统时钟之上创建自己的时钟,用于控制周期性线程。
我们的演示系统的源代码看似比较简单,但实际上却是建立在一个庞大的时间系统之上的,这个时间系统是整个操作系统活动的动力。
我们的演示程序将演示控制计算机的时钟计时( 每400毫秒显示一次 ),计时10次后返回初始菜单。
3.线程调度
在此个演示程序中,一开始有两个线程,它们所采取的调度算法是时间片轮转法,与此同时还有一个优先级最高的线程在休眠,它每400毫秒睡醒一次,打断此时处于运行状态的线程,那个优先级最高的线程转执行态,等它执行完它的时间片之后,前两个线程继续执行。
4.中断响应
这个例子演示了操作系统对外设的支持,我们选择了串口。演示这个例子,需要两台计算机和一根串口线,其中一台计算机安装我们的演示系统,另一台需要有一个向串口发送信息的软件,此软件我们是用Delphi编写的,运行在Windows 98系统下,两台计算机用我们自己制作的串口线相连。演示程序的一开始,有两个优先级相同的线程在时间片轮转。当我们从另一台装有Windows 98的机器上给演示机发送信息时,原来处于运行状态的两个线程被中断,演示机会成功的收到发送机发送的信息,然后等待操作人员的处理,是否继续运行该例子或者退出。