当前位置:Linux教程 - Linux文化 - OSKit包装系统驱动程序 第一章

OSKit包装系统驱动程序 第一章


第一章 OSKit概述

1. OSKit简介

通常情况下,开发一个操作系统是一件非常复杂的事情,操作系统的开发人员不仅要开发那些他们感兴趣的部分,还要花费大量的时间去开发一些他们并不感兴趣而不得不去开发的部分,如装入程序、启动程序、以及一些底层硬件的驱动程序等等。OSKit的开发者们开发它的初衷也正是为了解决这个问题。

OSKit是由美国犹它大学Flux研究小组开发的一套设计操作系统的工具包。它之所以被称之为一个工具包而不是一个操作系统,是因为它最初的设计意图就是要为操作系统的开发者们提供一套可重用的模块,使操作系统的开发者们可以很快的把精力集中于实际的操作系统的问题,如作业控制、进程间通信、文件系统和安全机制等等。

对于很多种情况,OSKit本身就是一个操作系统,它提供了操作系统中所需要的几乎所有模块,那么作为一个操作系统的工具包,它和真正的操作系统的差别又在哪里呢?按照OSKit的开发人员的话说就是"在我们看来,'操作系统'和'操作系统工具包'之间的分界线是:哪些是操作系统的开发人员真正想写的,而哪些是他们不得不写而实际上并不想写的。"从这一点我们也可以看出,对于不同的操作系统开发人员来说,因为他们感兴趣的部分不同,他们所使用的部分自然也就不同。OSKit是一个模块化的工具包,使用它的时候并不一定要使用它的全部,而可以仅仅去使用其中的一部分。

在这里,有一点是需要说明一下的,通常情况下,操作系统的源码是不能依赖于某些函数库的,因为C语言库(如GNU C Library)实际上是要依靠操作系统提供的某些功能才能够正常工作的。OSKit与这些函数库之间的一点差别就是:OSKit本身在设计时就注意到了这个问题,其中的函数在运行时都没有对其运行环境作出任何的假设,因此,这些函数是可以在操作系统的代码中调用的。

OSKit是一个自由软件,也可以被称为"开放源代码"软件,OSKit的主要部分是在GNU Public License(GPL)下发布的。但因为OSKit中使用了大量的其它操作系统(如Mach、FreeBSD、Linux等)的源代码,因此OSKit中这些部分则受到它们原有的版权协议的保护。


2. OSKit构成

2.1 结构概述

OSKit从整体上可以分为三个部分,第一部分是接口,第二部分是函数库,第三部分是部件库。但实际上,因为这三部分之间相辅相成的关系,在源代码中它们的划分并不是十分的清晰,在很多情况下,划分是非常随意的。不过,下面我们姑且仍然按照这样的划分来描述一下OSKit的整体结构。

2.2 接口

OSKit的接口是面向对象的,它采用了部件对象模型(COM)来定义。COM的基础是界面(Interfaces),所谓界面,就是一套方法,通过它们,无需知道对象的内部结构就可以访问直接访问对象。COM接口与部件及对象的实现无关,完全是独立的。用C语言是如何实现COM机制的呢?由于本文的重点不在这个方面,因此这里我只作一个简单的介绍。

OSKit中的COM接口是一个不透明的struct结构,它的实际大小和内容对于调用者来说都是未知的。它的第一个元素必须是一个指向一个函数表格的指针,这个函数表格中是该COM对象可以使用的方法。实际上,这个函数表格也是一个struct结构,结构中的每一个元素都是一个指向函数的指针。下面是一个很简单的例子,它定义了OSKit中块输入/输出对象(blkio)的界面。

/* 这是用户看到的COM接口 */
struct oskit_blkdev
{
  struct oskit_blkdev_ops *ops;
};
typedef struct oskit_blkdev oskit_blkdev_t;

/* 这是函数表,该COM接口所支持的方法 */
struct oskit_blkdev_ops
{
  /* COM-specified IUnknown interface operations */
  OSKIT_COMDEC* (*query)(oskit_blkdev_t *dev,
        const struct oskit_guid *iid,
        void **out_ihandle);
  OSKIT_COMDECL_U (*addref)(oskit_blkdev_t *dev);
  OSKIT_COMDECL_U (*release)(oskit_blkdev_t *dev);

  /* Base fdev device interface operations */
  OSKIT_COMDEC* (*getinfo)(oskit_blkdev_t *fdev,
        oskit_devinfo_t *out_info);
  OSKIT_COMDEC* (*getdriver)(oskit_blkdev_t *fdev,
        oskit_driver_t **out_driver);

  /* Block device interface operations */
  OSKIT_COMDEC(*open)(oskit_blkdev_t *dev, unsigned mode,
        struct oskit_blkio **out_blkio);
};

/* OS Environment中块设备接口的GUID */
extern const struct oskit_guid oskit_blkdev_iid;
#define OSKIT_BLKDEV_IID OSKIT_GUID(0x4aa7df82, 0x7c74,\
    0x11cf, 0xb5, 0x00, 0x08, 0x00, 0x09, 0x53, 0xad, 0xc2)

/* 以下定义的是较为友好的访问上述接口的方法 */
#define oskit_blkdev_query(dev, iid, out_ihandle) \
    ((dev)->ops->query((oskit_blkdev_t *)(dev), \
    (iid), (out_ihandle)))
#define oskit_blkdev_addref(dev) \
    ((dev)->ops->addref((oskit_blkdev_t *)(dev)))
#define oskit_blkdev_release(dev) \
    ((dev)->ops->release((oskit_blkdev_t *)(dev)))
#define oskit_blkdev_getinfo(fdev, out_info) \
    ((fdev)->ops->getinfo((oskit_blkdev_t *)(fdev), \
    (out_info)))
#define oskit_blkdev_getdriver(fdev, out_driver) \
    ((fdev)->ops->getdriver((oskit_blkdev_t *)(fdev), \
    (out_driver)))
#define oskit_blkdev_open(dev, mode, out_blkio) \
    ((dev)->ops->open((oskit_blkdev_t *)(dev), (mode), \
    (out_blkio)))

OSKit中定义的接口非常多,本文将要详细描述的,也正是OSKit中的一套非常重要的接口OS Environment(osenv)。它实际上是系统内核与驱动程序之间的接口,通过OS Environment,系统内核为驱动程序的运行提供了必要的支持。同时,系统也通过驱动程序所提供的COM接口来访问设备。

2.3 函数库

OSKit的函数库为操作系统提供最基本的低级服务。在OSKit的函数库中,很少使用OSKit的COM接口,而是用普通的C语言的函数实现的。这样操作系统的设计者只要直接调用这些函数就可以了。OSKit的函数库在设计上尽量公开,可以让使用者很容易的看到这些函数的设计细节。

OSKit中的函数库主要有一下几个:
liboskit_c:这是一个非常小的C语言库,它在受限制的OS环境中提供了一些通用的C函数,比如基本的字符串、内存和格式化的输入、输出工具等。如果需要更强大的C库,OSKit中还提供了FreeBSD的C库liboskit_freebsd_c。
liboskit_kern:这个函数库中包括建立一个基本的操作系统内核运行环境,为陷入、中断等提供了缺省的处理程序。这个库包括了很多在写核心代码时非常有用的通用函数,如访问某个特殊的处理器寄存器的函数,建立并维护页表,在不同的处理器模式之间转换(如x86的实模式和保护模式)等。此外它还提供了便于的在开发时使用的源码级内核调试机制。
liboskit_smp:更多的内 核支持代码,用于多处理器的系统环境。
liboskit_com:处理COM 接口的工具函数和一套通用的包装部件。
liboskit_dev:这个库提供了驱动程序和由其它操作系统引入的部件(如网络、文件系统)所需要的"glue code"中的函数的缺省实现。在OSKit的文档中,把这一部分放在了函数中,但通过对OSKit的源代码的分析,这一部分实际上使用了大量的COM接口。这一部分中的函数的实现是为了使用liboskit_kern函数库的简单内核所设计的,当需要精心制作的内核时,目标系统必须要重写此函数库中的部分或全部代码。

2.4 部件库

OSKit的部件库提供了比较高层的功能,这些高层的功能以标准的、面向对象的"黑盒"方式设计。部件库通常只向使用者公开一些相关的公共调用接口。例如,在Linux和BSD驱动程序部件库中,每一个完整的驱动程序仅仅实现为一个单独的函数调用,这个函数调用用来初始化并注册驱动程序。目标系统将通过OSKit的面向对象的COM 接口来与这些部件进行交互。

OSKit部件库的这种设计策略,可以有效的将大量已经存在的系统(如Linux和BSD)中的已经写好的代码合并进来,并隐藏原有环境的细节,使操作系统的设计者们方便的使用它们。

OSKit中的主要的部件库有:
liboskit_posix:增加对一个POSIX构造的系统会实现的系统调用的支持。例如:open、read和write等。这些POSIX操作被映射到相应的OSKit COM接口上。最小化的C库和FreeBSD C库都依赖POSIX库来提供所需要的系统级操作。
liboskit_freebsd_c:起源于FreeBSD的完整的类POSIX C库,提供了单线程和多线程的配置。在需要支持那些调用了类POSIX的函数或需要线程安全时,这个库可以用来替换最小化的C库liboskit_c。FreeBSD的"系统调用"层由POSIX库liboskit_posix提供,而高层的代码则没有作改动。
liboskit_freebsd_m:完整的标准数学库(从FreeBSD的libm中取得)。通常那些使用了浮点数的程序都需要这个库中的函数的支持。
liboskit_fsnamespace:文件系统名字空间库,为应用程序提供了"namei"风格的转换,如高层的mount 和unmount兼容。多种部件的绝对和相对路径名(有斜线的)被转换成为oskit_file和oskit_dir COM对象。
liboskit_rtld:运行时链接、装入库,允许ELF格式的OSKit内核装入共享库(.so文件)。
liboskit_lmm:一个灵活的存储管理库,它可以管理物理内存或虚拟内存。这个库支持很多OS级代码需要的功能,例如多种内存类型、分配优先权、和随意的放置已经分配的块的约束。
liboskit_amm:地址映射管理库用于管理收集到的资源,通常收集的每一个元素都有一个名字(地址)和一些属性。有可能被地址映射管理的资源的例子有交换空间和进程虚拟地址空间。
liboskit_svm:简单虚存库。使用AMM 库定义了一个简单的用于单独地址空间的虚存接口,它提供内存保护和为块设备如一个硬盘分区进行分页。(不被支持的redzone.c为单线程的内核提供了一个栈redzone,不需要SVM。)
liboskit_threads:这个库提供了多线程的内核支持,包括POSIX线程、同步、调度和栈保护。调度使用了标准的POSIX Round-Robin和FIFO。试验性地支持CPU遗传调度(CPU inheritance scheduling),这是一种用于随意的调度策略的分层架构,但它还不是很完整很强壮的。
liboskit_memdebug:这个库提供了一个用于调试的malloc,它可以检测多种与内存分配有关的错误(如overruns、使用已经释放的内存块等)。
liboskit_gprof:使OSKit的内核可以收集关于它自己的剖析数据并在运行结束时报告的的运行期支持代码。当编译内核时使用"-pg"选项就可以收集剖析数据。
liboskit_diskpart:一个能够识别多种常见的磁盘分区方案并生成一个完整的映射的通用库。这个库提供了一个简单的方法让操作系统可以找到相关的或者是"感兴趣的"磁盘分区,就如提供简单的高级方法使用不同的命名策略去访问任何磁盘分区,BSD和Linux兼容的名字策略在缺省的情况下是被支持的。
liboskit_fsread:一个简单只读文件系统解释库,它不同的常见的文件系统,包括BSD FFS、Linux ext2fs和MINIX的文件系统。这个库典型地被用于和分区库一起使用,为操作系统提供一种简单的方法从硬盘或软盘上读取程序和数据。此外,即使是在一些不需要它的操作系统里,这个功能经常在启动时需要。这部分代码在构造boot loader时也是很有用的。
liboskit_exec:一个通用的可执行程序解释器和装入器,它支持流行的可执行程序格式,如a.out和ELF。(即使是通常不需要装入可执行程序的微内核系统,通常也必须有一种方法装入第一个用户态的程序,OSKit的小巧、简单的可执行程序解释器很适合于这个目的。)
liboskit_linux_fs:包装了Linux 2.0.29的文件系统部分的代码。包括Linux VFS层支持的ext2fs(一个Linux系统上主要使用的文件系统)和其它很多PC上的被Linux支持的文件系统。
liboskit_netbsd_fs:包装了NetBSD 1.2的文件系统部分的代码,包括BSD VFS层支持的本地的FFS文件系统。
liboskit_memfs:一个基于内存的文件系统。
liboskit_freebsd_net:包装了FreeBSD 2.1.7.1的网络代码。包括套接字层和协议。
liboskit_bootp:这个库提供了一个简单的接口执行BOOTP协议(RFC 1048/1533),以客户的以太网卡的硬件地址为基础,从服务器取回一套规范的参数。
liboskit_linux_dev:包装了Linux 2.0.29的设备驱动程序代码。当前包括超过50种块设备(SCSI、IDE)和网络驱动,它被包装成使用OSKit的设备驱动架构。。
liboskit_freebsd_dev:包装了FreeBSD 2.1.7.1的设备驱动程序代码。当前包括8各TTY(虚拟控制台和串口线,包括鼠标)驱动。
liboskit_wimpi:基于MGR的简单的分层窗口系统,只用简单的画图和窗口管理操作。
liboskit_*video*:基本的视频支持,有两种实现:一种包装了全部SVGALIB 1.3.0,另一种基于XFree86 3.3.1,但只支持S3的驱动程序。

2.5 OSKit的整体结构图

上面我们介绍了OSKit的构成,那么OSKit的整体结构图是什么样的呢?这里我们给出一个OSKit的整体结构图,这个整体结构图并没有包括OSKit的全部部件,而只是其中一些比较主要的部分,但是从中我们仍然可以加深对于OSKit的了解。

本文要详细说明的就是上图中左下角用黑线框住的部分。


3. OSKit的运行环境

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

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

非纯执行模块。这些模块中使用了全局变量或有可能改变全局共享状态,例如liboskit_kern(核心支持库)中的许多函数建立和访问全局处理器寄存器和数据结构,因此它们是非纯执行模块。非纯执行模块有以下特点:

非纯函数和部件可能依赖于全局状态,如全局变量、静态变量、处理器中特殊寄存器等。
除非有明确的声明,非纯函数和部件是不可重入的,并且运用在多线程系统中也是不安全的。为了在一个多线程/多处理器环境中使用这些函数和部件,目标操作系统必须提供适当的同步代码。

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

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

此外,OSKit的执行环境还有以下特点:

在一段时间内,部件中可以存在多个活动进程,但在某时刻只有一个进程被执行。

目标操作系统给每个活动进程提供一个独立堆栈,这个堆栈在阻塞函数运行时被保留。只有在操作完成后,对部件的调用返回时才放弃该堆栈。

部件中的代码总是运行在两个级别中之一,进程级或中断级。有意思的是一些部件的函数和方法只能在进程级被调用,而另一些只能在中断级被调用,还有的能在任何级别被调用。调用的细节属于接口描述的一部分。

部件中无论进程级或中断级的操作都能被部件中的中断处理程序中断,除非代码调用osenv_intr_disable屏蔽了中断。

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

当目标操作系统在一个部件内中断一个进程级的活动时,在继续这个活动前,操作系统必须执行完这个中断级别的活动。同理,若一个中断级的活动被中断,那么最近的中断级别的活动必须在继续前一个中断级别的活动之前完成。

部件中运行在中断级别的代码不能调用目标操作系统提供的阻塞回调函数;只有非阻塞的回调函数能够在中断级别被调用。