Sinos是基于Intel 80386系列CPU的独立操作系统,运行于32位保护模式。除了PC预置的BIOS外,其运行不依赖于任何外部软件,实现了作为真正PC操作系统的绝大部分特征。
与大部分自制操作系统走模仿Linux路线、引用大量Linux源代码或者以Linux为基础进行的开发不同,Sinos是一个完全从零开始设计的操作系统,完全没有Linux成分,而且其设计思想更倾向于Microsoft。例如,系统使用FAT16文件系统、内核级线程、按优先级调度等,都参考了Windows的设计思想。
尽管Sinos只是实验性质的操作系统,但在其设计理念上强调“现代操作系统”概念,力争在技术层面与主流操作系统相比没有明显的空白,同时也力求在结构设计上符合操作系统的发展趋势。
虚拟内存机制
从Intel推出80386微处理器开始,PC就拥有了与大型机相类似的段页式内存管理能力,为现代操作系统实现虚拟内存管理机制提供了硬件基础。现代的PC操作系统普遍支持32位线性地址和进程间内存地址空间的隔离,Sinos也做到了这一点。
与Windows和Linux一样,Sinos同样没有完全使用80386的段页式内存管理支持,而是使用固定的指向零地址的段选择子和描述符,从而跳过了分段机制,只使用硬件的分页内存管理支持。
在各进程独立的4GB虚拟地址中,高端的1GB被用作共享的核心态内存,低端3GB为用户态内存,这样可以提高陷入内核时的性能以及增进内核代码间通讯的效率,Windows与Linux也使用该地址划分方式。
因此,在内存的保护机制上,Sinos跳过了段特权级(Segment Privilege Level)检查,只使用分页支持中的保护机制,即只在页表项中设置特权位(U/S位)来限制用户态代码对高端1GB内存页的访问。
虚拟内存机制的另一个重点是页交换,即在物理内存不够时把内存页面内容传送到交换文件中去,在该页被再次引用时通过缺页故障(PF, Page Fault)机制把页面内容再次重新调入物理内存。
由于操作的一致性,Sinos中对可执行映像的加载也是通过缺页机制完成的。通过这种机制,在加载可执行映像时只需简单地把文件中各段映射到相应的用户地址空间,在使用时便会自动加载,可以做到物理内存的按需分配式管理。
在线性内存地址的分配方面,Sinos把核心态内存和用户态内存区别对待。核心态地址空间采用被广泛应用的伙伴树算法以求得分配效率,而用户态地址则简单地使用首适算法以节约内存空间。
内核对象管理
同Windows一样,Sinos中内核数据单元也是以对象形式管理的。其实对象管理器可以看作是内存管理器的一部分,它帮助组织核心态下各结构体数据,以便以内存页的形式管理。
当某个模块需要动态分配某种固定长度的数据结构,例如进程管理器需要为每一个进程分配PCB(Process Control Block,进程控制块)时,它就在该模块初始化时向对象管理器注册一个新的对象类型,并且指定PCB的大小,对象管理器便会为该类型进行相关的控制初始化。当需要真正为结构体分配空间时,对象管理器会自动在该相同类型对象所占用的内存页中搜寻可用空间并进行协调,最终合理地为对象分配物理内存空间。
Sinos内核模块间约定,每个模块建立的控制结构对象均使用唯一的句柄(Handle)来标识自身,该句柄是一个32位数值。这样,模块与模块之间的相互调用便是通过句柄值来传递数据,可以有效地避免由于模块间数据结构定义的相互引用而造成的问题。
内核级线程调度
同Windows类似,Sinos中任务调度的基本单位是线程。所有进程中的线程按照其优先级统一调度,系统不区分线程所在进程,这样的线程称之为内核级线程。
在线程调度算法中,系统同样采用Windows的调度思想,只是稍稍简化。系统把所有的线程分成32个(Windows是48个)优先级统一调度,其优先级按优先级数排列,即0-31级中31级优先级最高,0级最低(分配给IDLE线程)。其中内核线程使用高端的16个,用户级线程使用低端的16个,这样可以保证内核线程中诸如中断延迟处理线程等关键线程可以优先处理。
所有的线程按照其优先级,分别组织成一个双向循环链表,即调度系统中一共有32组链表。调度时系统按照优先级高低,从高到低依次调度;对于优先级相同的就绪线程,系统简单地使用轮转的办法调度。
可扩展的文件系统框架
早期的操作系统,诸如早期版本的Unix和MS-DOS,都拥有自己的文件系统,其系统的文件管理模块与磁盘文件系统的格式紧密结合,它们只能操作自己的文件系统。
而现代的PC操作系统,为适应软硬件技术的发展,一般都支持两种以上的文件系统格式。这样,在这些操作系统内部就都有对文件系统进行抽象的机制,以便统一处理各磁盘文件系统乃至网络文件系统。
Sinos采用一种类似驱动的做法,每种类型的文件由一个叫做“文件供应器”的模块负责。通过回调函数的形式,操作系统把所有的文件操作请求,按照其文件名前缀转发给不同的文件供应器,具体的文件读写由供应器负责。这样,系统框架中就只需要处理诸如文件控制块(File Control Block,FCB)同步、文件权限判断之类的抽象工作,底层数据的读取和控制便具有很大的灵活性。
举例来说,某系统有两个硬盘分区,分别是FAT16和NTFS格式的,那么在系统初始化时,可以注册一个“C:”文件供应器,指定到为FAT16而写的回调函数上;注册一个“D:”的文件供应器,指定到为NTFS格式所写的回调函数上。同理,如果该系统需要访问Web,可以通过驱动注册一个“http:”的文件供应器;如果需要下载FTP,可以注册一个“ftp:”文件供应器,等等。
开放的设备驱动模型
作为PC操作系统,Windows必须面对PC平台上数以千计的形形色色的硬件,每种硬件都需要特定的驱动程序。为了保证这些各不相同的硬件驱动以一组相同的接口协同工作,同时为了让驱动程序员集中精力于硬件控制上,Windows定义了一套极为复杂的驱动模型,帮助各硬件驱动与其它驱动以及操作系统的其它模块正确高效地协同工作。
作为一个实验性质的操作系统,Sinos没必要也不太可能参照Windows庞大的驱动模型来设计自己的驱动体系,这里需要一个灵活的轻量级解决方案。
Sinos驱动模型的设计原则就是简洁。如果某个驱动对应于某个硬件,则一切对该硬件进行的操作都通过一个定义好的回调(Callback)函数,完全交给驱动来处理。包括硬件的初始化、启动、停止、检测等操作,驱动框架对其不作干预,由驱动处理一切。
这么做的优点是代码框架简洁明了,而缺点在于容错性差,对于驱动的正确性要求较高,因为系统检测驱动的操作正确与否的机会比较少。
驱动之间的通讯是通过文件方式完成的。比如上一节所举的例子,两个文件系统的供应器都需要基于同一个磁盘的扇区操作,那么每个物理磁盘也对应于一个设备文件,其所在的IDE总线控制器(假设为IDE硬盘)便由驱动实现为一个文件供应器(如“ATA3”),其下有若干个磁盘文件(如“ATA3/HDD0”,“ATA3/HDD1”)。对磁盘的操作便可处理成对文件的读写,从而统一驱动之间的通讯接口。
基于信号量和消息队列的同步系统
在Sinos中,基本同步对象是信号量(Semaphore)。由于信号量这种同步对象的功能强大,其它类型的同步对象,诸如事件(Event),互斥量(Mutex)等都可以在外部以运行库的形式衍生出来。
作为补充,系统中还利用x86硬件特性简单实现了对变量“加一”和“减一”两个互锁操作(Interlocked Operation)。
消息队列是现代操作系统中另一个被广泛使用的同步机制。由于内核模块本身工作就依赖于数个系统消息队列,如延迟中断处理队列、设备命令列队等,所以在内核中实现了统一的消息队列机制。
PE格式可执行映像
PE(Portable Executable)是Microsoft为其Windows NT/95系列操作系统设计的可执行文件格式,是一种相当优秀的可执行映像格式。
Sinos采用PE格式是出于开发工具的考虑。为了使用现有的编译器,Sinos必须使用编译器对应的可执行映像的格式。由于Windows下有不少优秀的编译器和PE工具,所以最终选用了Microsoft 的PE格式作为系统的可执行映像格式。