内核是操作系统最基本的部分。它是为众多应用程序提供对计算机
硬件的安全访问的一部分
软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间。内核的分类可分为
单内核和双内核以及
微内核。严格地说,内核并不是
计算机系统中必要的组成部分。
基本简介
内核,是一个
操作系统的核心。是基于硬件的第一层软件扩充,提供操作系统的最基本的功能,是操作系统工作的基础,它负责管理系统的进程、
内存、设备
驱动程序、文件和
网络系统,决定着系统的性能和稳定性。
现代
操作系统设计中,为减少系统本身的开销,往往将一些与
硬件紧密相关的(如中断处理程序、
设备驱动程序等)、基本的、公共的、运行频率较高的模块(如时钟管理、
进程调度等)以及
关键性数据结构独立开来,使之常驻内存,并对他们进行保护。通常把这一部分称之为操作系统的
内核。
程序可以直接地被调入
计算机中执行,这样的设计说明了设计者不希望提供任何硬件
抽象和操作系统的支持,它常见于早期计算机系统的设计中。最终,一些辅助性程序,例如程序加载器和
调试器,被设计到机器核心当中,或者固化在只读
存储器里。这些变化发生时,操作系统内核的
概念就渐渐明晰起来了。
(概述图片来源:)
历史发展
Linux的第一个公开版本是1991年10月的0.02版本,两个月以后,在1991年12月,
Linux发布了0.11版本,这是第一个可以不依赖于
Minix就可以使用的独立内核。
0.12版本发布一个月以后,在3月,
版本号跳到了0.95,反映出系统正变得成熟,不仅如此,直到两年后,也就是1994年3月,具有
里程碑意义的1.0.0才完成。
大约从这时起开始使用两“路”编号方法标注内核的开发,偶数号的内核(比如1.0、2.2、2.4、2.6)是稳定的,“产品”型号,同时,奇数号的内核版本(1.1、2.3)是前沿的或者“发展中的”内核。一个稳定的内核发布以后几个月就开始新内核的开发工作。然而,2.5的开发工作是在2.4完成后几十个月以后才开始的。
post-halloween文档的大部分讨论内容是用户需要注意的主要改变,以及需要更新的系统工具(为了利用它们)。关心这一信息人的主要是那些期望提前了解2.6内核中有哪些内容的Linux发行商,还有终端用户,这可以让他们确定为了能利用新部件是否有需要升级的程序。
KernelJanitors项目保持了一个列表,内容是需要修复的较小缺陷和解决方法。这些缺陷解决方法中大部分是由于向内核打较大的补丁时需要改动很多部分代码而导致的,比如有些地方会影响设备驱动程序。那些新近从事内核开发的人开始时的工作可以选择列表中的条目,这样让他们可以通过小项目学习如何编写内核代码,同时有机会为社区做出贡献。
还有,在另一个预发布的项目中,JohnCherry追踪了在对每个已经发布的内核版本进行编译时发现的错误和警告。这些编译统计数字随着时间的流逝一直持续下降,而且,以系统的形式来发布这些结果使得所取得的进展一目了然。在很多情况下,可以像使用KernelJanitors列表一样来利用这些警告和错误消息中的一部分,因为编译错误通常是由小的缺陷引起的,需要一些努力去修复。
最后,还有AndrewMorton的“must-fix”列表。由于他已经被选定为2.6内核发布后的
维护者,他运用他的特权概括地列出了那些他认为在最终的2.6内核发布前最迫切需要解决方案的问题。must-fix列表中包含了内核Bugzilla系统中的缺陷,需要完成的部件,以及其他已知的问题,这些问题如不解决将阻碍2.6发布。这一信息可以帮助指明在新内核发布前还需要哪些步骤;对那些关心这一万众期待的2.6内核发布何时能完成的人来说,它还可以提供有价值的信息。
内核分类
单内核
单内核(Monolithic kernel),是个很大的
进程。它的内部又能够被分为若干模块(或是层次或其他)。但是在运行的时候,它是个单独的二进制大映象。其模块间的通讯是通过直接调用其他模块中的函数实现的,而不是消息传递。
单内核结构在
硬件之上定义了一个高阶的抽象界面,应用一组原语(或者叫
系统调用)来实现操作系统的功能,例如
进程管理,文件系统,和
存储管理等等,这些功能由多个运行在核心态的模块来完成。
尽管每一个模块都是单独地服务这些操作,内核代码是高度集成的,而且难以编写正确。因为所有的模块都在同一个
内核空间上运行,一个很小的bug都会使整个系统崩溃。然而,如果开发顺利,单内核结构就可以从运行效率上得到好处。
很多现代的单内核结构内核,如
Linux和
FreeBSD内核,能够在运行时将模块调入执行,这就可以使扩充内核的功能变得更简单,也可以使内核的核心部分变得更简洁。
单内核结构是非常有吸引力的一种设计,由于在同一个地址空间上实现所有低级操作的系统控制代码的复杂性的效率会比在不同地址空间上实现更高些。 单核结构正趋向于容易被正确设计,所以它的发展会比微内核结构更迅速些。
单内核结构的例子:传统的UNIX内核----例如
伯克利大学发行的版本,Linux内核。
微内核
微内核(Microkernelkernel)结构由一个非常简单的硬件抽象层和一组比较关键的原语或系统调用组成,这些原语仅仅包括了建立一个系统必需的几个部分,如
线程管理,地址空间和
进程间通信等。
微核的目标是将
系统服务的实现和系统的基本操作规则分离开来。例如,进程的输入/输出锁定服务可以由运行在微核之外的一个服务组件来提供。这些非常模块化的用户态服务器用于完成操作系统中比较高级的操作,这样的设计使内核中最核心的部分的设计更简单。一个服务组件的失效并不会导致整个系统的崩溃,内核需要做的,仅仅是重新启动这个组件,而不必影响其它的部分
微内核将许多OS服务放入
分离的
进程,如文件系统,
设备驱动程序,而进程通过消息传递调用OS服务。微内核结构必然是多线程的,第一代微内核,在核心提供了较多的服务,因此被称为'胖微内核',它的典型代表是
MACH。它既是GNU HURD也是APPLE SERVER OS的核心,可以说,蒸蒸日上.第二代为
微内核只提供最基本的
OS服务,典型的OS是QNX,QNX在理论界很有名,被认为是一种先进的OS。
微内核只提供了很小一部分的硬件抽象,大部分功能由一种特殊的用户态程序:
服务器来完成。微核经常被用于
机器人和医疗器械的嵌入式设计中,因为它的系统的关键部分都处在相互分开的,被保护的存储空间中。这对于单核设计来说是不可能的,就算它采用了运行时加载模块的方式。
微内核的例子:
AIX,
BeOS,L4微内核系列,.
Mach中用于GNU Hurd和
Mac OS X,
Minix,MorphOS,
QNX,RadiOS,
VSTa。
混合内核
混合内核它很像微内核结构,只不过它的的组件更多的在
核心态中运行,以获得更快的执行速度。
混合内核实质上是微内核,只不过它让一些微核结构运行在用户空间的代码运行在内核空间,这样让内核的运行效率更高些。这是一种妥协做法,设计者参考了微内核结构的系统运行速度不佳的理论。然而后来的实验证明,纯微内核的系统实际上也可以是高效率的。大多数
现代操作系统遵循这种设计范畴,微软公司开发的
Windows操作系统就是一个很好的例子。另外还有XNU,运行在苹果Mac OS X上的内核,也是一个混合内核。
混合内核的例子: BeOS 内核 ,
DragonFly BSD,ReactOS 内核
外内核
外内核系统,也被称为纵向结构操作系统,是一种比较极端的设计方法。
外内核这种内核不提供任何硬件抽象操作,但是允许为内核增加额外的
运行库,通过这些运行库
应用程序可以直接地或者接近直接地对硬件进行操作。
它的设计理念是让
用户程序的设计者来决定硬件接口的设计。外内核本身非常的小,它通常只负责系统保护和
系统资源复用相关的服务。
传统的内核设计(包括单核和
微核)都对硬件作了抽象,把硬件资源或设备驱动程序都隐藏在硬件抽象层下。比方说,在这些系统中,如果分配一段物理
存储,应用程序并不知道它的实际位置。
而外核的目标就是让应用程序直接请求一块特定的
物理空间,一块特定的
磁盘块等等。系统本身只保证被请求的资源当前是空闲的,应用程序就允许直接存取它。既然外核系统只提供了比较低级的硬件操作,而没有像其他系统一样提供高级的硬件抽象,那么就需要增加额外的运行库支持。这些运行库运行在外核之上,给用户程序提供了完整的功能。
理论上,这种设计可以让各种操作系统运行在一个外核之上,如Windows和Unix。并且设计人员可以根据运行效率调整系统的各部分功能。
外核设计还停留在研究阶段,没有任何一个
商业系统采用了这种设计。几种概念上的操作系统正在被开发,如
剑桥大学的Nemesis,
格拉斯哥大学的Citrix系统和瑞士计算机科学院的一套系统。
麻省理工学院也在进行着这类研究。
比较
单内核结构是非常有吸引力的一种设计,由于在同一个地址空间上实现所有复杂的低阶操作系统控制代码的效率会比在不同地址空间上实现更高些。
20世纪90年代初,单内核结构被认为是过时的。把Linux设计成为单内核结构而不是微内核,引起了无数的争议。
单核结构正倾向于设计不容易出错,所以它的发展会比微内核结构更迅速些。两个阵营中都有成功的案例。
尽管Mach是众所周知的多用途的微内核,人们还是开发了除此之外的几个微内核。
L3是一个演示性的内核,只是为了证明微内核设计并不总是低运行速度。它的后续版本L4,甚至可以将Linux内核作为它的一个进程,运行在单独的地址空间。
QNX是一个从20世纪80年代,就开始设计的微内核系统。它比Mach更接近微内核的理念。它被用于一些特殊的领域;在这些情况下,由于软件错误,导致系统失效是不允许的。例如航天飞机上的机械手,还有研磨望远镜镜片的机器,一点点失误就会导致上千美元的损失。
很多人相信,由于Mach不能够解决一些提出微内核理论时针对的问题,所以微内核技术毫无用处。Mach的爱好者表明这是非常狭隘的观点,遗憾的是似乎所有人都开始接受这种观点。
优点
抽象隐藏
内核提供一种硬件抽象的方法来完成对
硬件操作,因为这些操作是非常复杂的,硬件抽象隐藏了复杂性,为
应用软件和硬件提供了一套简洁,统一的接口,使程序设计更为简单。
源代码管理
历史上,从来没有出现过用于Linux内核的正式的
源代码管理或修正控制系统。实际上,很多开发者实现了他们自己的修正控制器,但是并没有官方的LinuxCVS档案库,让LinusTorvalds检查加入代码,并让其他人可以由此获得代码。修正控制器的缺乏,常常会使发行版本之间存在“代沟”,没有人真正知道加入了哪些改变,这些改变是否能很好地融合,或者在即将发行的版本中哪些新内容是值得期待的。通常,如果更多的开发者可以像了解他们自己所做的改变一样了解到那些变化,某些问题就可以得到避免。
非常有必要使用一个实时的、集中的档案库来保存对Linux内核的最新更新。每一个被内核接受的改变或者补丁都被作为一个改变集被追踪。终端用户和开发者可以保存他们自己的源文件档案库,并根据需要可以通过一个简单的
命令用最新的改变集进行更新。对开发者来说,这意味着可以始终使用最新的代码拷贝。测试人员可以使用这些逻辑的改变集合来确定哪些变化导致了问题的产生,缩短调试所需要的时间。甚至那些希望使用最新内核的用户也可以直接利用实时的、集中的
档案库,因为一旦他们所需要的部件或缺陷修复加入到内核中,他们就可以马上进行更新。当代码融合到内核时,任何用户都可以提供关于这些代码的即时反馈和缺陷报告。
并行开发
随着Linux内核的成长,变得更加复杂,而且吸引更多开发者将注意力集中到内核的特定方面的专门开发上来,出现了另一个开发Linux方法的有趣改变。在2.3内核版本的开发期间,除了由LinusTorvalds发行的主要的一个内核树之外,还有一些其他的内核树。
在2.5的开发期间,内核树出现了爆炸式的增长。由于使用源代码管理工具可以保持开发的同步并行进行,这样就可能实现开发的部分并行化。为了让其他人在他们所做的改变被接受之前可以进行测试,有一些开发需要并行化。那些保持自己的树的内核维护者致力于特定的组件和目标,比如
内存管理、NUMA部件、改进扩展性和用于特定体系结构的代码,还有一些树收集并追踪对许多小缺陷的纠正。
这种并行开发模型的优点是,它使得需要进行重大改变的开发者,或者针对一个特定的目标进行大量类似改变的那些开发者可以自由地在一个受控环境中开发,而并不影响其他人所用内核的稳定性。当开发者完成工作后,他们可以发布针对
Linux内核当前版本的补丁,以实现到此为止他们所完成的改变。这样,社区中的测试人员就可以方便地测试这些改变并提供反馈。当每一部分都被证明是稳定的之后,那些部分可以单独地,或者甚至同时全部地,融合到主要Linux内核中。
(内核结构图相册部分图片来源:)
代码覆盖分析
新工具为内核提供了代码覆盖分析的功能。
覆盖分析表明,在一个给定的测试运行时,内核中哪些行代码被执行。更重要的是,覆盖分析提示了内核的哪些部分还根本没有被测试到。这个数据是重要的,因为它指出了需要再编写哪些新测试来测试内核的那些部分,以使内核可以得到更完备的测试。
大量信息
在为将来的2.6Linux内核进行开的过程中,除了这些自动化的信息管理方法之外,开放源代码社区的不同成员还收集和追踪了数量惊人的信息。
例如,在KernelNewbies站点上创建了一个状态列表,来保持对已经提出的内核新部件的追踪。这个列表包含了以状态排序的条目,如果它们已经完成了,则说明它们已经包含在哪个内核中,如果还没有完成,则指出还需要多长时间。列表上很多条目的链接指向大型项目的Web站点,或者当条目较小时,链接指向一个解释相应部件的电子邮件信息的拷贝。
测试
应用测试
测试背景
过去,
Linux内核测试方法围绕开放源代码开发模型进行。由于代码一经发布后就公开给其他开发者进行审查,因此从来没有出现过一个与其他形式的软件开发类似的正式的验证周期。这种方法背后的理论依据是“TheCathedralandtheBazaar”中所谓的“Linus法则”(请查阅参考资料以获得相关的参考),这一法则的内容为“众人的眼光是雪亮的”。换句话说,高力度的审查可以找出大部分真正的大问题。
然而实际上,内核有很多
复杂的相互联系。即使进行了足够力度的审查,还是会漏过很多严重的缺陷。此外,最新的内核一经发布,终端用户可以(也经常是)下载并使用。2.4.0发布时,社区中很多人都提议进行更有组织的测试,以保证特定测试和代码审查的强度。有组织的测试包括运用
测试计划、测试过程中的可重复性等等。使用所有的三种方法比最初只使用两种方法会带来更高的代码质量。
测试项目
最早对Linux开始进行有组织测试的贡献者是Linux测试项目(LinuxTestProject,LTP)。这个项目的目的是通过更有组织的测试方法提高Linux的质量。这个测试项目的一部分是自动测试套件的开发。LTP开发的主要测试套件也叫做Linux测试项目。2.4.0内核发布时,LTP测试套件只有大约100个测试。随着2.4和2.5版本Linux的发展与成熟,LTP测试套件也正在发展和成熟。当前,Linux测试项目包括超过2000个测试,而且这个数字还在增长。
回归测试
在2.5的开发周期中,Linux测试项目所采用的另一个
项目是,用
LTP测试套件对Linux内核执行持续多日的回归测试。人们用BitKeeper创建了一个实时的、集中的档案库,以随时可以获得Linux内核的
快照。在没有使用BitKeeper和快照时,测试人员不得不等到内核发布后才可以开始测试。内核只要发生了改变,测试人员就可以进行测试。
使用自动化工具来执行持续多日的回归测试的另一个优点是,和上一次测试相比变化较小。如果发现了一个新的回归缺陷,通常会容易地检测出这个缺陷可能是哪个改变导致的。
同样,由于是最新的改变,因此它在开发者的脑海中印象还比较深——希望这能让他们更容易地记起并修订相应的代码。或许Linus法则应该有这样一个结论,有一些缺陷比其他缺陷更容易被发现,因为那些正是持续多日的内核回归测试所发现并处理的那些。在开发周期中和实际发布之前能够每天进行这些测试,这就使那些只关注完整发行版本的测试者可以将精力集中于更严重和耗时的缺陷。
测试系统
另外一个名为开放源代码开发实验室(OpenSourceDevelopmentLabs,OSDL)的团队也为Linux测试做出了重要的贡献。2.4内核发布后不久,OSDL创建了一个叫做可扩展测试平台(ScalableTestPlatform,STP)的系统。STP是一个自动化的测试平台,让开发者和测试者可以运行OSDL硬件之上的系统所提供的测试。开发者甚至可以使用这个系统来测试他们自己的针对内核的补丁。可扩展测试平台简化了测试的步骤,因为
STP可以构建内核、设置测试、
运行测试,并收集结果。然后得到结果以进行深入地比较。很多人无法接触大型系统,比如具有8个处理器的SMP机器,而通过STP,任何人都可以在像这样的大型系统上运行测试,这个系统(STP)的另一个好处就在于此。
追踪缺陷
自从2.4发布以来,对Linux内核的有组织测试最大的改进之一是缺陷追踪。过去,在Linux内核中发现的缺陷会报告给Linux内核邮件列表,报告给特定组件或者特定体系的邮件列表,或者直接报告给维护发现缺陷的那部分代码的个人。随着开发和测试Linux的人数的增加,这个系统的不足之处很快就暴露了出来。在以前,除非人们对缺陷的报告可以惊人地维持下去,缺陷经常被遗漏、遗忘或者忽略。
OSDL安装了一个缺陷追踪系统,来报告和
追踪Linux内核的缺陷。系统经过了配置,这样当某个组件的缺陷被报告时,那个组件的维护者就会得到通知。维护者既可以接受并修复那个缺陷,或重新指定缺陷(如果最终确定实际上那是内核另外一部分的缺陷),也可以排除它(如果最终确定并不是真正的缺陷,比如错误配置的系统)。报告给邮件列表的缺陷还有丢失的危险,因为越来越多的
电子邮件涌向那个列表。然而,在缺陷追踪系统中,始终有对每一个缺陷及其当前状态的记录。