单内核:将内核从整体上作为一个大过程实现,并同时运行在一个单独的地址空间。所有的内核服务都在一个地址空间运行,相互之间直接调用函数,简单高效。微内核:功能被划分成独立的过程,过程间通过IPC进行通信。模块化程度高,一个服务失效不会影响另外一个服务。Linux是一个单内核结构,同时又吸收了微内核的优点:模块化设计,支持动态装载内核模块。Linux还避免了微内核设计上的缺陷,让一切都运行在内核态,直接调用函数,无需消息传递。
Linux大部分都是单内核的
操作系统内核可能是微内核,也可能是单内核(后者有时称之为宏内核Macrokernel)。按照类似封装的形式,这些术语定义如下:
l 微内核(Microkernel kernel)――在微内核中,大部分内核都作为单独的进程在特权状态下运行,他们通过消息传递进行通讯。在典型情况下,每个概念模块都有一个进程。因此,假如在设计中有一个系统调用模块,那么就必然有一个相应的进程来接收系统调用,并和能够执行系统调用的其他进程(或模块)通讯以完成所需任务。
在这些设计中,微内核部分经常只但是是个消息转发站:当系统调用模块要给文档系统模块发送消息时,消息直接通过内核转发。这种方式有助于实现模块间的隔离。(某些时候,模块也能够直接给其他模块传递消息。)在一些微内核的设计中,更多的功能,如I/O等,也都被封装在内核中了。但是最根本的思想还是要保持微内核尽量小,这样只需要把微内核本身进行移植就能够完成将整个内核移植到新的平台上。其他模块都只依赖于微内核或其他模块,并不直接直接依赖硬件。
微内核设计的一个长处是在不影响系统其他部分的情况下,用更高效的实现代替现有文档系统模块的工作将会更加容易。我们甚至能够在系统运行时将研发出的新系统模块或需要替换现有模块的模块直接而且迅速的加入系统。另外一个长处是无需的模块将不会被加载到内存中,因此微内核就能够更有效的利用内存。
l 单内核(Monolithic kernel)――单内核是个很大的进程。他的内部又能够被分为若干模块(或是层次或其他)。但是在运行的时候,他是个单独的二进制大映象。其模块间的通讯是通过直接调用其他模块中的函数实现的,而不是消息传递。
单内核的支持者声称微内核的消息传递开销引起了效率的损失。微内核的支持者则认为因此而增加的内核设计的灵活性和可维护性能够弥补任何损失。
我并不想讨论这些问题,但必须说明很有趣的一点是,这种争论经常会令人想到前几年CPU领域中RISC和CISC的斗争。现代的成功CPU设计中包含了任何这两种技术,就像Linux内核是微内核和单一内核的混合产物相同。Linux内核基本上是单一的,但是他并不是个纯粹的集成内核。前面一章所介绍的内核模块系统将微内核的许多长处引入到Linux的单内核设计中。(顺便提一下,我考虑过一种有趣的情况,就是Linux的内核模块系统能够将系统内核转化成为简单的不传递消息的微内核设计。虽然我并不赞成,但是他仍然是个有趣的想法。)
为什么Linux必然是单内核的呢?一个方面是历史的原因:在Linus的观点看来,通过把内核以单一的方式进行组织并在最初始的空间中运行是相当容易的事情。这种决策避免了有关消息传递体系结构,计算模块装载方式等方面的相关工作。(内核模块系统在随后的几年中又进行了不断地改进。)
另外一个原因是充足的研发时间的结果。Linux既没有研发时间的限制,也没有深受市场压力的发行进度。任何的限制只有并但是分的对内核的修改和扩充。内核的单一设计在内部实现了充分的模块化,在这种条件下的修改或增加都并不怎么困难。而且问题还在于没有必要为了追求尚未证实的可维护性的微小增长而重写Linux的内核。(Linus曾多次特别强调了如下的观点:为了这点利益而损耗速度是不值得的。)后面章节中的部分内容将周详的重新考虑充足研发时间的效果。
假如Linux是纯微内核设计,那么向其他体系结构上的移植将会比较容易。实际上,有一些微内核,如Mach微内核,就已成功的证实了这种可移植性的长处。实际的情况是,Linux内核的移植虽然不是很简单,但也绝不是不可能的:大约的数字是,向一个全新的体系结构上的典型的移植工作需要30,000到 60,000行代码,再加上不到20,000行的驱动程式代码。(并不是任何的移植都需要新的驱动程式代码。)粗略的计算一下,我估计一个典型的移植平均需要50,000行代码。这对于一个程式员或最多一个程式小组来说是力所能及的,能够在一年之内完成。虽然这比微内核的移植需要更多的代码,但是 Linux的支持者将会提出,这样的Linux内核移植版本比微内核更能够有效的利用底层硬件,因而移植过程中的额外工作是能够从系统性能的提高上得到补偿的。