当前位置:Linux教程 - Linux文化 - OSKit的线程机制 第一章

OSKit的线程机制 第一章


第一章 绪 论


1.1 简介

OSKit是由美国犹它大学计算机科学系FLUX研究组编写的一套模块化部件和库函数,用于架构操作系统内核、服务器以及其他的OS级软件。我们设想一下,在一个操作系统的研发项目中,底层模块的开发工作会占去大部分时间,并耗费掉开发人员的大部分精力。而OSKit的出现恰恰弥补了这个缺陷,其设计意图是提供一套可重用的模块,让使用者避开复杂的底层,把精力集中在他们感兴趣的问题上,也就是说,当开发人员拿到OSKit之后,便立刻拥有了一个完整而且安全的核心,使他们可以集中精力研发操作系统的高层次问题,如作业控制、虚存、IPC、文件系统、系统安全以及高级语言(如Java、Lisp或ML)等。这样可以大大丰富操作系统的应用层,为用户提供更多更好的服务,提高操作系统的运行效率,增强操作系统的安全性和稳定性,从而使你的操作系统更加具有魅力。

对于站在操作系统技术最前沿的多线程编程和成熟的作业控制系统,以及时下最流行的嵌入式操作系统,OSKit都提供了支持。通过几个月以来对美国犹它大学计算机科学系FLUX研究组网站的追踪,我们注意到OSKit的版本大约每三个月就更新一次,我们在毕业设计初期拿到的是99年7月的版本,而我们写演示程序是用的2000年5月的版本,在此期间又出现了99年12月版和2000年2月版,而且在每次发布的版本里都有许多新的算法公布,还有许多老版本中的BUG被修改;这说明OSKit不但一直处于操作系统开发平台的最前沿,而且其自身也在不断的完善。

通过对OSKit深入细致的分析与研究,我们发现犹他大学的开发人员们从一开始就确定了自己的目标,那就是对OSKit进行模块化,仿佛Windows中的动态连接库一样,让后来者即使不使用OSKit中的某一部分,仍然可以使用其余部分来完成他们的目标。这样的设计思路十分灵活,为开发者和使用者都提供了便利。


1.2 安装与配置OSKit

当我们从犹他大学得到OSkit的五月版时,它是个压缩包,使用tar在linux下解开后,就得到了源码,这需要我们在linux下重新编译。

目前,编译OSKit需要以下的工具:

GNU make
GNU CC (gcc) 2.95.2版。

注意:到目前为止,我们使用的Redhat Linux 6.2中的GCC并不是2.95.2,如果要成功编译OSKit,需要先将我们的GCC升级。当然,如果你使用的是Turbo linux 6.0的话,它提供的就是GCC 2.95.2。

在开始编译OSKit之前,我们必须注意一点,那就是要对源码进行一点改动,让它支持实时操作,即将 /oskit20000505/threads/MakeFlage 中的:
# OSKIT_CFLAGS + = -DPTHREAD_SCHED_EDF -DPTHREAD_REALTIME 中的注释符号 # 去掉。

由于我们的环境是Linux,所以论文中的根目录用"/"表示,而不是Windows中的"\"。

由于我们是在intel 586上编译,而我们的目标机要支持到intel 386,所以我们应该在OSKit的目录下键入:

./configure ――host=i586 ――target=i386
此后键入make,编译OSKit。

当OSKit编译好以后,你可以用"make install"命令来安装它。在缺省的情况下,这些库会被安装到/usr/local/lib中,而头文件会被安装到/usr/local/include中,除非你在配置时使用了--prefix选项来指定它。所有的OSKit头文件都安装在oskit/子目录中(例如/usr/local/include/oskit),不会和已经存在的任何头文件冲突。即使是库被安装在主库的目录中(如/usr/local/lib),所有的库文件都有前缀oskit_,这可以避免和其它不相关的库混淆在一起。


1.3 使用OSKit

一开始,为了能让大家尽快熟悉OSKit,犹他大学的研发人员们给出了许多很具代表性的例子供使用者进行试验,而我们的课题组也编写了一个小型的操作系统,供学习研究之用。

OSKit中库的设计是很经典的,开发者们花费了大量时间清楚地标出了每个库与其它库的依赖关系,这使得OSKit不但保留了操作系统的原汁原味,而且还把库之间的相关性降低到了最低限度,使得使用者可以随意增减其中的库函数。实际上,在很多情况下,特别是在某些函数库中,为了有效的使用OSKit,必须替换掉其中的一些函数或符号,以适应我们的执行环境。要重载某个库中的函数或者符号,只要在你的核心或应用程序中再定义一遍就可以了,LINK程序会确保去使用你所定义的函数和符号。由于在Linux下的编译器是靠读取Makefile文件来确定编译连接的对象以及规则,所以我们认为直接修改Makefile文件要比修改OSKit源码更加方便而且安全,这与Windows下的工程文件十分相似。

如果使用OSKit所提供的模块化部件和库函数编写出了自己的操作系统核心代码,并且对在LINUX下用GCC编译源码有所了解的话,就可以借助OSKit为使用者提供的一整套核心制作命令和LINUX提供的GCC来生成自己的核心文件zImage,然后使用LINUX命令DD将核心写入一张1.44MB的软盘,重新启动计算机,从软盘引导就可进入我们自己编写的操作系统了。

另外,考虑到用户环境复杂多样的实际情况,OSKit还提供了对许多种操作系统的支持,包括Linux、Mach 、BSD或者MS-DOS,当我们用GCC将源码编译成"filename.o"的格式之后,使用OSKit提供的不同的核心制作命令来生成不同操作系统下的核心,其具体实现是由mkbsdimage、mklinuximage和mkdosimage来完成的。这些脚本在OSKit的安装和配置阶段会根据你指定的环境自动安装完毕。在每一个脚本运行的时候都有各自不同的参数。


1.4 OSKit 导航图

OSKit所提供的功能被分成三类:接口函数库部件库

1.4.1 接口

OSKit的接口非常清晰,并且是面向对象的。这些接口全部被定义在C语言的头文件中,我们分别对其加了必要的注释,以方便使用者快速掌握OSKit。这些接口被OSKit的各个部件共享,从而体现了各部件之间的一致性。操作系统的开发人员既可以直接使用它,也可以在其上定义目标操作系统的接口结构。

1.4.2 函数库

OSKit的函数库以C语言中面向函数的方式提供基本的服务。一个库中的函数对另一个库中函数的依赖关系已经被降至最低的程度,如果这种依赖是不可避免的话,那他们已经被很清楚地标注出来,展现给了操作系统的开发人员。函数库很少使用OSKit的面向对象的COM接口,取而代之的是在C的头文件中定义自己的面向函数的接口。这种设计策略为操作系统开发人员提供了灵活性和极大的技术支持;要适应任何特殊的操作系统环境,底层的OSKit工具必须具有可扩展性。

1.4.3 部件库

OSKit的部件库使使用者能站在更高的层次来编程,即面向对象的"黑盒"方式。尽管OSKit的"部件"在缺省情况下也是被打包成为普通的链接库,但它们的结构则是被设计程面向对象的,而不是传统的面向函数的方式。与OSKit的函数库相比,部件库通常只对外开放一些相关的公用调用接口,而不是大量的功能。例如,在Linux和BSD的驱动程序部件库里,每一个完整的驱动程序仅呈现为一个单独的函数调用,这个调用用来初始化并注册驱动程序。

1.4.4 执行环境

OSKit中的许多部件在核心和用户方式下都可以使用,这就需要对部件的执行环境作出定义,例如部件什么时候可以嵌套进入等。此外,OSKIT使用了许多其它操作系统的代码,例如设备驱动程序和网络协议栈,都是原封不动的从原有的核心如BSD和Linux中借用来的,OSKit通过附加代码模拟原始执行环境使的这些执行模块比它们原始执行环境更简单,用户也不需要详细了解原执行环境的细节。下面对OSKIT的每种执行模块进行简单的介绍。

纯执行模块:这是OSKit执行环境中最简单的一个模块,这些部件或函数中没有全局变量或静态变量,只使用和处理由目标环境传递来的数据。例如函数strlen,它只通过目标环境传递给它的字符串指针求出字符串的长度,并将其返回,它只接触参数数据域,而不影响目标环境。当这些函数使用的数据集是分离的,它们可以安全地同时被调用,而不需要同步,反之则不行。例如对重叠的目标缓冲区并发使用memcpy调用时不安全的。

非纯执行模块:这些模块中使用了全局变量或有可能改变全局共享状态,例如liboskit_kern(核心支持库)中的许多函数建立和访问全局处理器寄存器和数据结构,因此它们是非纯执行模块。非纯执行模块有以下特点:非纯函数和部件可能依赖于全局状态,如全局变量,静态变量,处理器中的特殊寄存器等。除非有明确的声明,非纯函数和部件是不可重入的,并且运用在多线程系统中也是不安全的。为了在一个多线程/多处理器环境中使用这些函数和部件,目标操作系统必须提供适当的同步代码。

阻塞模块:它扩展了非纯模块来支持非抢先的多线程,这些模块中有一类可重入的函数称为阻塞函数,在这模块中,除非明确声明为非阻塞函数,否则函数是阻塞的。为了在一个完全抢先的、可中断的或者多处理器的环境中使用阻塞模块,必须在进入模块前加锁,在退出模块时将锁释放。

可中断阻塞模块:在它之中每个部件都是一个单线程的执行域,在一个给定的时刻,只有一个(虚拟的或者物理的)CPU可以执行部件中的代码。例如:在一个多处理器系统中,在进程级别下,任意时刻在一个部件集内只有一个CPU被允许执行;这能够通过在部件前后放置全局锁来实现。

在一段时间内,部件中可以存在多个活动进程,但在某时刻只有一个进程被执行。目标操作系统给每个活动进程提供一个独立堆栈,这个堆栈在阻塞函数运行时被保留。只有在操作完成后,对部件的调用返回时才放弃该堆栈。部件中的代码总是运行在两个级别中之一,进程级或中断级。有意思的是一些部件的函数和方法只能在进程级别被调用,而另一些只能在中断级别被调用,还有的能在任何级别被调用。调用的细节属于接口描述的一部分。

部件中无论进程级别或中断级别的操作都能被部件中的中断处理程序中断,除非代码调用osenv_intr_disable屏蔽了中断。当部件在进程级运行时,OSKIT假定中断开放,部件在处理过程中可能临时屏蔽掉中断,但必须在返回到目标操作系统前重新激活。同样,当部件在中断级运行时,OSKIT假定中断被屏蔽,但是部件可以在目标操作系统允许其它中断级别的活动中断该部件时重新激活中断。

当目标操作系统在一个部件内中断一个进程级别的活动时,在继续这个活动前,操作系统必须执行完这个中断级别的活动。同理,若一个中断级的活动被中断,那么最近的中断级别的活动必须在继续前一个中断级别的活动之前完成。部件中运行在中断级别的代码不能调用目标操作系统提供的阻塞回调函数;只有非阻塞的回调函数能够在中断级别被调用。