操作系統內核-宏內核與微內核(轉載)
聲明:本文從CSDN轉載,原文鏈接為:https://blog.csdn.net/Silencegll/article/details/51496158
操作系统内核可能是微内核,也可能是单内核(后者有时称之为宏内核Macrokernel)。按照类似封装的形式,这些术语定义如下:
单内核:也称为宏内核。将内核从整体上作为一个大过程实现,并同时运行在一个单独的地址空间。所有的内核服务都在一个地址空间运行,相互之间直接调用函数,简单高效。微内核:功能被划分成独立的过程,过程间通过IPC进行通信。模块化程度高,一个服务失效不会影响另外一个服务。Linux是一个单内核结构,同时又吸收了微内核的优点:模块化设计,支持动态装载内核模块。Linux还避免了微内核设计上的缺陷,让一切都运行在内核态,直接调用函数,无需消息传递。
Linux大部分都是单内核的。
微内核(Microkernel kernel)――在微内核中,大部分内核都作为单独的进程在特权状态下运行,他们通过消息传递进行通讯。在典型情况下,每个概念模块都有一个进程。因此,假如在设计中有一个系统调用模块,那么就必然有一个相应的进程来接收系统调用,并和能够执行系统调用的其他进程(或模块)通讯以完成所需任务。
在这些设计中,微内核部分经常只但是是个消息转发站:当系统调用模块要给文档系统模块发送消息时,消息直接通过内核转发。这种方式有助于实现模块间的隔离。(某些时候,模块也能够直接给其他模块传递消息。)在一些微内核的设计中,更多的功能,如I/O等,也都被封装在内核中了。但是最根本的思想还是要保持微内核尽量小,这样只需要把微内核本身进行移植就能够完成将整个内核移植到新的平台上。其他模块都只依赖于微内核或其他模块,并不直接直接依赖硬件。
微内核设计的一个长处是在不影响系统其他部分的情况下,用更高效的实现代替现有文档系统模块的工作将会更加容易。我们甚至能够在系统运行时将研发出的新系统模块或需要替换现有模块的模块直接而且迅速的加入系统。另外一个长处是无需的模块将不会被加载到内存中,因此微内核就能够更有效的利用内存。
单内核(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内核移植版本比微内核更能够有效的利用底层硬件,因而移植过程中的额外工作是能够从系统性能的提高上得到补偿的。
这种特别设计的权衡也不是很轻松就能够达到的,单内核的实现策略公然违背了传统的看法,后者认为微内核是未来发展的趋势。但是由于单一模式(大部分情况下)在Linux中运行状态良好,而且内核移植相对来说比较困难,但没有明显地阻碍程式员团体的工作,他们已热情高涨地把内核成功的移植到了现存的大部分实际系统中,更不用说类似掌上型电脑的一些看起来很不实际的目标了。只要Linux的众多特点仍然值得移植,新的移植版本就会不断涌现。
所有的Unix内核都同宗同源,并且提供相同的API,现代的Unix内核存在许多设计上的相似之处。Unix内核几乎毫无例外的都是一个不可分割的静态可执行块(文件)。也就是说,它们必须以完整、单独的可执行块的形式在一个单独的地址空间中运行。
Unix内核几乎都需要硬件系统提供页机制以管理内存。这种页机制可以加强内存空间的保护,并保证每个进程都可以运行于不同的虚地址空间上。
单内核与微内核设计之比较
操作系统内核可以分为两大设计阵营:单内核和微内核(第三阵营外内核,主要用在科研系统中,但也逐渐在现实世界中壮大起来)。
单内核是两大阵营中一种较为简单的设计,在1980年之前,所有的内核都设计成单内核。所谓单内核就是把它从整体上作为一个单独的大过程来实现,并同时运行在一个单独的地址空间。因此,这样的内核通常以单个静态二进制文件的形式存放于磁盘。所有内核服务都在这样的一个大内核空间中运行。内核之间的通信是微不足道的,因为大家都运行在内核态,并身处同一地址空间:内核可以直接调用函数,这与用户空间没有什么区别。这种模式的支持者认为单模块具有简单和高性能的特点。大多数Unix系统都设计为单模块。
另一方面,微内核并不作为一个单独的大过程来实现。相反,微内核的功能被划分为独立的过程,每个过程叫做一个服务器。理想情况下,只有强烈请求特权服务的服务器才运行在特权模式下,其他服务器都运行在用户空间。不过,所有的服务器都保持独立并运行在各自的地址空间。因此,就不可能像单模块内核那样直接调用函数,而是通过消息传递处理微内核通信:系统采用了进程间通信(IPC)机制,因此,各种服务器之间通过IPC机制互通消息,互换“服务”。服务器的各自独立有效地避免了一个服务器的失效祸及另一个。
同样,模块化的系统允许一个服务器为了另一个服务器而换出。因为IPC机制的开销比函数调用多,又因为会涉及内核空间到用户空间的上下文切换,因此,消息传递需要一定的周期,而单内核中简单的函数调用没有这些开销。基于此,付之于实际的微内核系统让大部分或全部服务器位于内核,这样,就可以直接调用函数,消除频繁的上下文切换。Windows NT内核和Mach(Mac OS X的组成部分)是微内核的典型实例。不管是Windows NT还是Mac OS X,都在其新近版本中不让任何微内核服务器运行在用户空间,这违背了微内核设计的初衷。
Linux是一个单内核,也就是说,Linux内核运行在单独的内核地址空间。不过,Linux汲取了微内核的精华:其引以为豪的是模块化设计、抢占式内核、支持内核线程以及动态装载内核模块的能力。不仅如此,Linux还避其微内核设计上性能损失的缺陷,让所有事情都运行在内核态,直接调用函数,无需消息传递。至今,Linux是模块化的、多线程的以及内核本身可调度的操作系统。实用主义再次占了上风。
当Linus和其他内核开发者设计Linux内核时,他们并没有完全彻底地与Unix诀别。他们充分地认识到,不能忽视Unix的底蕴(特别是 Unix的 API)。而由于Linux并没有基于某种特定的Unix,Linus和他的伙伴们对每个特定的问题都可以选择已知最理想的解决方案—在有些时候,当然也可以创造一些新的方案。以下是对Linux内核与Unix各种变体的内核特点所作的分析比较:
·Linux支持动态加载内核模块。尽管Linux内核也是单内核,可是允许在需要的时候动态地卸除和加载部分内核代码。
·Linux支持对称多处理(SMP)机制,尽管许多Unix的变体也支持SMP,但传统的Unix并不支持这种机制。
·Linux内核可以抢占(preemptive)。与传统的Unix不同,Linux内核具有允许在内核运行的任务优先执行的能力。在其他各种Unix产品中,只有Solaris和IRIX支持抢占,但是大多数传统的Unix内核不支持抢占。
·Linux对线程支持的实现比较有意思:内核并不区分线程和其他的一般进程。对于内核来说,所有的进程都一样—只不过其中的一些共享资源而已。
·Linux提供具有设备类的面向对象的设备模型、热插拔事件,以及用户空间的设备文件系统(sysfs)。
·Linux忽略了一些被认为是设计得很拙劣的Unix特性,像STREAMS,它还忽略了那些实际上已经根本不会使用的过时标准。
·Linux体现了自由这个词的精髓。现有的Linux特性集就是Linux公开开发模型自由发展的结果。如果一个特性没有任何价值或者创意很差,没有任何人会被迫去实现它。相反的,在Linux的发展过程中已经形成了一种值得称赞的务实态度:任何改变都要针对现实中确实存在的问题,经过完善的设计并有正确简洁的实现。于是,许多其他现代Unix系统包含的特性,如内核换页机制,都被毫不迟疑的引入进来。
不管Linux和Unix有多大的不同,它身上都深深地打上了Unix烙印
以下是摘自wiki的一段关于宏内核的介绍
宏内核有时也称单内核,原文为英文的Monolithic kernel。
宏内核是操作系统内核架构的一种,此架构的特性是整个内核程序都是以内核空间(Kernel Space)的身份及监管者模式(Supervisor Mode)来运行。相对于其他类型的操作系统架构,如微内核架构或混内核架构等,这些内核会定义出一个高级的虚拟接口,由该接口来涵盖描述整个计算机硬件,这些描述会集合成一组硬件描述用词,有时还会附加一些系统调用,如此可以用一个或多个模块来实现各种操作系统服务,如进程管理、共时(Concurrency)控制、存储器管理等。
即使有的宏内核将其运作从整体性运作拆分成几个服务模块,并让各模块各自运作,其操作系统的代码依然是高度紧密的,很难修改成其他类型的操作系统架构。此外,所有的模块也都在同一块寻址空间内运行,倘若某个模块有错误、瑕疵(Bug),运行时就会损及整个操作系统运作。反过来,如果宏内核架构的操作系统在开发设计时相当完善,并经测试验证后具有高度可靠性,则操作系统内的各软件组件因具有高度紧密性,如此在系统的低级运作上将格外有效率。
可加载性的模块
现在多数采行宏内核架构设计的操作系统,如OpenVMS、Linux、FreeBSD、以及Solaris等,都已经能在运作运行阶段中,以动态方式来加载(Load)、卸载(Unload)可运行的模块,不过这些模块是属于二进制代码的层次,或称镜像层次,而非内核架构的层次。即使宏内核进行模块化转化,也不会与微内核或混内核架构的内核产生区分上的混淆,因为微内核、混内核的模块是属于系统架构的层次。
就实务上,动态加载/卸载模块的作法,等于是用一种较简易的方式来弹性管控运行中的操作系统内核,若没有动态加载/卸载机制,操作系统的内核想要进行任何的调整、变换,都必须重新开机才能达成。因此模块化是必然且必要的,如此才能让内核功效轻松地扩展、延伸,此外也能适时减轻硬件的运行运作负担。
另外,有些整块性操作系统为了让它的内核空间达到最小化,也会运用动态加载/卸载机制来达成此一目标。
操作系统内核可能是微内核,也可能是单内核(后者有时称之为宏内核Macrokernel)。按照类似封装的形式,这些术语定义如下:
单内核:也称为宏内核。将内核从整体上作为一个大过程实现,并同时运行在一个单独的地址空间。所有的内核服务都在一个地址空间运行,相互之间直接调用函数,简单高效。微内核:功能被划分成独立的过程,过程间通过IPC进行通信。模块化程度高,一个服务失效不会影响另外一个服务。Linux是一个单内核结构,同时又吸收了微内核的优点:模块化设计,支持动态装载内核模块。Linux还避免了微内核设计上的缺陷,让一切都运行在内核态,直接调用函数,无需消息传递。
Linux大部分都是单内核的。
微内核(Microkernel kernel)――在微内核中,大部分内核都作为单独的进程在特权状态下运行,他们通过消息传递进行通讯。在典型情况下,每个概念模块都有一个进程。因此,假如在设计中有一个系统调用模块,那么就必然有一个相应的进程来接收系统调用,并和能够执行系统调用的其他进程(或模块)通讯以完成所需任务。
在这些设计中,微内核部分经常只但是是个消息转发站:当系统调用模块要给文档系统模块发送消息时,消息直接通过内核转发。这种方式有助于实现模块间的隔离。(某些时候,模块也能够直接给其他模块传递消息。)在一些微内核的设计中,更多的功能,如I/O等,也都被封装在内核中了。但是最根本的思想还是要保持微内核尽量小,这样只需要把微内核本身进行移植就能够完成将整个内核移植到新的平台上。其他模块都只依赖于微内核或其他模块,并不直接直接依赖硬件。
微内核设计的一个长处是在不影响系统其他部分的情况下,用更高效的实现代替现有文档系统模块的工作将会更加容易。我们甚至能够在系统运行时将研发出的新系统模块或需要替换现有模块的模块直接而且迅速的加入系统。另外一个长处是无需的模块将不会被加载到内存中,因此微内核就能够更有效的利用内存。
单内核(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内核移植版本比微内核更能够有效的利用底层硬件,因而移植过程中的额外工作是能够从系统性能的提高上得到补偿的。
这种特别设计的权衡也不是很轻松就能够达到的,单内核的实现策略公然违背了传统的看法,后者认为微内核是未来发展的趋势。但是由于单一模式(大部分情况下)在Linux中运行状态良好,而且内核移植相对来说比较困难,但没有明显地阻碍程式员团体的工作,他们已热情高涨地把内核成功的移植到了现存的大部分实际系统中,更不用说类似掌上型电脑的一些看起来很不实际的目标了。只要Linux的众多特点仍然值得移植,新的移植版本就会不断涌现。
所有的Unix内核都同宗同源,并且提供相同的API,现代的Unix内核存在许多设计上的相似之处。Unix内核几乎毫无例外的都是一个不可分割的静态可执行块(文件)。也就是说,它们必须以完整、单独的可执行块的形式在一个单独的地址空间中运行。
Unix内核几乎都需要硬件系统提供页机制以管理内存。这种页机制可以加强内存空间的保护,并保证每个进程都可以运行于不同的虚地址空间上。
单内核与微内核设计之比较
操作系统内核可以分为两大设计阵营:单内核和微内核(第三阵营外内核,主要用在科研系统中,但也逐渐在现实世界中壮大起来)。
单内核是两大阵营中一种较为简单的设计,在1980年之前,所有的内核都设计成单内核。所谓单内核就是把它从整体上作为一个单独的大过程来实现,并同时运行在一个单独的地址空间。因此,这样的内核通常以单个静态二进制文件的形式存放于磁盘。所有内核服务都在这样的一个大内核空间中运行。内核之间的通信是微不足道的,因为大家都运行在内核态,并身处同一地址空间:内核可以直接调用函数,这与用户空间没有什么区别。这种模式的支持者认为单模块具有简单和高性能的特点。大多数Unix系统都设计为单模块。
另一方面,微内核并不作为一个单独的大过程来实现。相反,微内核的功能被划分为独立的过程,每个过程叫做一个服务器。理想情况下,只有强烈请求特权服务的服务器才运行在特权模式下,其他服务器都运行在用户空间。不过,所有的服务器都保持独立并运行在各自的地址空间。因此,就不可能像单模块内核那样直接调用函数,而是通过消息传递处理微内核通信:系统采用了进程间通信(IPC)机制,因此,各种服务器之间通过IPC机制互通消息,互换“服务”。服务器的各自独立有效地避免了一个服务器的失效祸及另一个。
同样,模块化的系统允许一个服务器为了另一个服务器而换出。因为IPC机制的开销比函数调用多,又因为会涉及内核空间到用户空间的上下文切换,因此,消息传递需要一定的周期,而单内核中简单的函数调用没有这些开销。基于此,付之于实际的微内核系统让大部分或全部服务器位于内核,这样,就可以直接调用函数,消除频繁的上下文切换。Windows NT内核和Mach(Mac OS X的组成部分)是微内核的典型实例。不管是Windows NT还是Mac OS X,都在其新近版本中不让任何微内核服务器运行在用户空间,这违背了微内核设计的初衷。
Linux是一个单内核,也就是说,Linux内核运行在单独的内核地址空间。不过,Linux汲取了微内核的精华:其引以为豪的是模块化设计、抢占式内核、支持内核线程以及动态装载内核模块的能力。不仅如此,Linux还避其微内核设计上性能损失的缺陷,让所有事情都运行在内核态,直接调用函数,无需消息传递。至今,Linux是模块化的、多线程的以及内核本身可调度的操作系统。实用主义再次占了上风。
当Linus和其他内核开发者设计Linux内核时,他们并没有完全彻底地与Unix诀别。他们充分地认识到,不能忽视Unix的底蕴(特别是 Unix的 API)。而由于Linux并没有基于某种特定的Unix,Linus和他的伙伴们对每个特定的问题都可以选择已知最理想的解决方案—在有些时候,当然也可以创造一些新的方案。以下是对Linux内核与Unix各种变体的内核特点所作的分析比较:
·Linux支持动态加载内核模块。尽管Linux内核也是单内核,可是允许在需要的时候动态地卸除和加载部分内核代码。
·Linux支持对称多处理(SMP)机制,尽管许多Unix的变体也支持SMP,但传统的Unix并不支持这种机制。
·Linux内核可以抢占(preemptive)。与传统的Unix不同,Linux内核具有允许在内核运行的任务优先执行的能力。在其他各种Unix产品中,只有Solaris和IRIX支持抢占,但是大多数传统的Unix内核不支持抢占。
·Linux对线程支持的实现比较有意思:内核并不区分线程和其他的一般进程。对于内核来说,所有的进程都一样—只不过其中的一些共享资源而已。
·Linux提供具有设备类的面向对象的设备模型、热插拔事件,以及用户空间的设备文件系统(sysfs)。
·Linux忽略了一些被认为是设计得很拙劣的Unix特性,像STREAMS,它还忽略了那些实际上已经根本不会使用的过时标准。
·Linux体现了自由这个词的精髓。现有的Linux特性集就是Linux公开开发模型自由发展的结果。如果一个特性没有任何价值或者创意很差,没有任何人会被迫去实现它。相反的,在Linux的发展过程中已经形成了一种值得称赞的务实态度:任何改变都要针对现实中确实存在的问题,经过完善的设计并有正确简洁的实现。于是,许多其他现代Unix系统包含的特性,如内核换页机制,都被毫不迟疑的引入进来。
不管Linux和Unix有多大的不同,它身上都深深地打上了Unix烙印
以下是摘自wiki的一段关于宏内核的介绍
宏内核有时也称单内核,原文为英文的Monolithic kernel。
宏内核是操作系统内核架构的一种,此架构的特性是整个内核程序都是以内核空间(Kernel Space)的身份及监管者模式(Supervisor Mode)来运行。相对于其他类型的操作系统架构,如微内核架构或混内核架构等,这些内核会定义出一个高级的虚拟接口,由该接口来涵盖描述整个计算机硬件,这些描述会集合成一组硬件描述用词,有时还会附加一些系统调用,如此可以用一个或多个模块来实现各种操作系统服务,如进程管理、共时(Concurrency)控制、存储器管理等。
即使有的宏内核将其运作从整体性运作拆分成几个服务模块,并让各模块各自运作,其操作系统的代码依然是高度紧密的,很难修改成其他类型的操作系统架构。此外,所有的模块也都在同一块寻址空间内运行,倘若某个模块有错误、瑕疵(Bug),运行时就会损及整个操作系统运作。反过来,如果宏内核架构的操作系统在开发设计时相当完善,并经测试验证后具有高度可靠性,则操作系统内的各软件组件因具有高度紧密性,如此在系统的低级运作上将格外有效率。
可加载性的模块
现在多数采行宏内核架构设计的操作系统,如OpenVMS、Linux、FreeBSD、以及Solaris等,都已经能在运作运行阶段中,以动态方式来加载(Load)、卸载(Unload)可运行的模块,不过这些模块是属于二进制代码的层次,或称镜像层次,而非内核架构的层次。即使宏内核进行模块化转化,也不会与微内核或混内核架构的内核产生区分上的混淆,因为微内核、混内核的模块是属于系统架构的层次。
就实务上,动态加载/卸载模块的作法,等于是用一种较简易的方式来弹性管控运行中的操作系统内核,若没有动态加载/卸载机制,操作系统的内核想要进行任何的调整、变换,都必须重新开机才能达成。因此模块化是必然且必要的,如此才能让内核功效轻松地扩展、延伸,此外也能适时减轻硬件的运行运作负担。
另外,有些整块性操作系统为了让它的内核空间达到最小化,也会运用动态加载/卸载机制来达成此一目标。