操作系统内核之争

1、内核分类

内核(Kernel)在计算机科学中是操作系统最基本的部分,主要负责管理系统资源。

中文版维基百科上将内核分为四大类:

单内核(宏内核);

微内核;

混合内核;

外内核。

宏内核

宏内核(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 NT、Windows 2000、Windows XP、Windows Server 2003以及Windows Vista等基于NT技术的操作系统。

外内核

外内核系统,也被称为纵向结构操作系统,是一种比较极端的设计方法。

外内核这种内核不提供任何硬件抽象操作,但是允许为内核增加额外的运行库,通过这些运行库应用程序可以直接地或者接近直接地对硬件进行操作。

它的设计理念是让用户程序的设计者来决定硬件接口的设计。外内核本身非常的小,它通常只负责系统保护和系统资源复用相关的服务。

传统的内核设计(包括单核和微核)都对硬件作了抽象,把硬件资源或设备驱动程序都隐藏在硬件抽象层下。比方说,在这些系统中,如果分配一段物理存储,应用程序并不知道它的实际位置。

而外核的目标就是让应用程序直接请求一块特定的物理空间,一块特定的磁盘块等等。系统本身只保证被请求的资源当前是空闲的,应用程序就允许直接存取它。既然外核系统只提供了比较低级的硬件操作,而没有像其他系统一样提供高级的硬件抽象,那么就需要增加额外的运行库支持。这些运行库运行在外核之上,给用户程序提供了完整的功能。

理论上,这种设计可以让各种操作系统运行在一个外核之上,如Windows和Unix。并且设计人员可以根据运行效率调整系统的各部分功能。

外核设计还停留在研究阶段,没有任何一个商业系统采用了这种设计。几种概念上的操作系统正在被开发,如剑桥大学的Nemesis,格拉斯哥大学的Citrix系统和瑞士计算机科学院的一套系统。麻省理工学院也在进行着这类研究。

2、宏内核和微内核的著名的争论

混合内核实质上也是微内核,而外内核是一种比较极端的设计方法,目前还处于研究阶段,所以我们就着重讨论宏内核与微内核两种内核。

这两种派系的代表人分别是Tanenbaum和Linus。 Tanenbaum的一段话比较通俗易懂的说明了什么叫微内核什么叫宏内核。 “老一点的操作系统都是宏内核的,也就是说,整个操作系统是一个运行在核心态的单独的a.out文件, 这个二进制文件包含进程管理,内存管理,文件系统以及其他。具体实例包括UNIX,MS-DOS,VMS,MVS, OS/360,MULTICS等等。 另一种便是微内核,在这种系统中,操作系统的大部分都运行在单独的进程,而且多数在内核之外。它们 之间通过消息传递来通信。内核的任务是处理消息传递,中断处理,底层的进程管理,以及可能的I/O。

关于宏内核和微内核,Linux的发明人linus Torvalds 和Minix的发明人人Andrew S. Tanenbaum从1992年开始就有一个争论,一直延续到今天,详细的可以到wiki上看事情的原委:https://en.wikipedia.org/wiki/Tanenbaum–Torvalds_debate

3、从fork实现看宏内核和微内核的区别

两个系统的内核是通过进程的创建FORK的实现来比较,因为进程的创建涉及到系统调用,内存管理,文件管理等系统的主要方面.因此通过比较FORK的实现可以大致看到内核的差别.

微内核的代表:Minix

在Minix中,操作系统的内核,内存管理,系统管理都有自己的进程表,每个部分的表包含了自己需要的域。表象是精确对应的,为了保持同步,在进程创建或结束时,这三个部分都要更新各自的表。由内存管理器协调。

系统启动后,kernel,mm,fs系统进程在各自的空间运行main()函数循环等待消息

While(TRUE)               

{
	…                      
	receive(ANY,&mm_in);               
	…
}

当一个FORK传给mm’main(),main()调用do_fork(),do_fork()函数把父进程的data segment和stack segment创造了一个精确副本给子进程,并把父进程的text segment 与子进程共享,然后在mm的进程表mproc[]中添加新进程,并设置各属性。添加完后发送消息给kernel(sys_fork(…))和 fs(tell_fs(…)).,kernel中的函数sys_task()接收到系统信息,调用do_fork(message * m_ptr),copy parent’proc struct to child.并设置进程在内核进程表中的属性。tell_fs()是内存管理器与文件系统之间的接口,tell_fs(…)调用 _taskcall(…),文件管理器接收到FORK系统消息,调用do_fork()函数,copy parent’fproc struct to child.并设置进程在文件进程表中的属性。这样整个进程的属性就设置完成.

在Minix创建新进程的过程中,可以看到一个很大特点,就是整个系统按功能分成几个部分,各模块之间利用消息机制通信,调用其他模块的函数必须通过目标模块的守护进程调用.

宏内核的代表:Linux

在Linux中,进程的结构如下:

Struct task_struct{               
	pid_t pid;               
	pid_t pgrp;               
	…               
	/* filesystem information */               
	struct fs_struct *fs;              
	/* memory management info*/               
	struct mm_struct *mm;               
	…
};

在Linux进程的结构定义中,task_struct包含了所有的信息,包括进程的内存情况,文件系统情况。在创建新进程时,系统调用sys_fork调用do_fork(…)函数.

int do_fork(unsigned long clong_flags,… )
{        
	struct task_struct *p;        
	p->pid = get_pid(clone_flags);        
	…        
	/* copy all the process information*/        
	copy_files(clone_flags,p);        
	copy_fs(clone_flags,p);        
	copy_mm(nr,clone_flags,p);        
	…
}

在创建进程时,do_fork函数把所有的工作完成,分配pid…号,拷贝父进程数据段,堆栈段,等等。Linux的进程创建过程是一个完整的过程,直接调用其他模块的函数,而不是消息传递。

Minix与Linux创建新进程的过程比较可以看出二者之间的区别,Minix是建立在分模块之上的,模块之间以信息传递联系。Linux内部也是分模块的,但在运行的时候,他是一个独立的二进制大映像,其模块间的通讯是通过直接调用其他模块中的函数实现的。宏内核与微内核的区别也就在这吧,微内核是一个信息中转站,自身完成很少功能,主要是传递一个模块对另一个模块的功能请求,而宏内核则是一个大主管,把内存管理,文件管理等等一股脑全部接管。

从理论上来看,微内核的思想更好些,微内核把系统分为各个小的功能块,降低了设计难度,系统的维护与修改也容易,但通信带来的效率损失是个问题。宏内核的功能块之间的耦合度太高造成修改与维护的代价太高,不过在目前的Linux里面还不算大问题,因为Linux目前还不算太复杂,宏内核因为是直接调用,所以效率是比较高的。

4、宏内核和微内核的本质区别

简单的介绍,宏内核(Monolithickernel)是将内核从整体上作为一个大过程来实现,所有的内核服务都在一个地址空间运行,相互之间直接调用函数,简单高效。微内核(Microkernel)功能被划分成独立的过程,过程间通过IPC进行通信,模块化程度高,一个服务失效不会影响另外一个服务。

两种设计各具特点,较早的一些操作系统都是用宏内核写成的,这样的内核通常以单个静态二进制文件的形式存放于磁盘,而后来随着模块化、分层思想的产生,人们开始对微内核感兴趣并持赞扬态度,认为微内核将成为内核的发展趋势。微内核的功能被划分为独立的过程,每个过程叫做一个服务器,各种服务器之间通过IPC机制互通消息,服务器的各自独立有效地避免了一个服务器的失效祸及另一个,而且这种独立机制更利于操作系统在不同硬件上的移植。

宏内核的示意图微内核的示意图

当然,微内核的这所有的优点都建立在因为消息传递开销而引起效率损失的前提下(IPC机制的开销比函数调用多,还会涉及到内核空间到用户空间的上下文切换),这些损失有一部分是靠着现在硬件效率的飞速提升而抵消着的,但这很明显是追不上宏内核的效率的。所以,有些使用微内核的操作系统(如新近版本的WindowsNT和MacOSX)让大部分或全部的服务器位于内核,这样就可以直接调用函数,消除频繁的上下文切换,但这已经违背了微内核设计的初衷。

而相反的,宏内核在通信上比较高效,因为大家都运行在内核态,并身处同一地址空间,内核可以直接调用函数,Linux和大多数的UNIX都为宏内核。对应的,宏内核不具有微内核的优点,宏内核的灵活性和可维护性不及微内核,而且移植起来并不简单(不过有些宏内核支持者认为这样的移植比微内核更能够有效地利用底层硬件)。

历史告诉我们,两种看似矛盾又各有长处的观点或解决方案总能找到结合点的。Cache的直接映射与关联映射结合产生了组关联映射;RISC和CISC也是各具优点,现代的好的CPU包括了这两种技术,将它们结合了起来。所以我相信宏内核与微内核也会最终找到结合点的。

Linux虽是宏内核,但已吸收了微内核的部分精华。Linux是模块化的、多线程的、内核本身可调度的系统,既吸收了微内核的精华,又保留了宏内核的优点,无需消息传递,避免性能损失。

此前所提到的微内核实现时让大部分服务器位于内核空间,也算是微内核朝宏内核靠拢的一个例子。

系统服务运行在内核空间的微内核

这两个例子都可以看到宏内核与微内核有结合的趋势,但还没有完全真正的结合成功,不过它们都已经同时具备了模块化和内核空间运行等的特征。

原文发布于微信公众号 - 大数据和云计算技术(jiezhu2007)

原文发表时间:2016-10-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

Flexera FlexNet Publisher中基于栈的缓冲区溢出漏洞分析

近日,安全人员在Flexera FlexNet Publisher(License Manager)中发现了一个基于栈的缓冲区溢出漏洞(CVE编号:CVE-20...

1837
来自专栏我是攻城师

关于SparkStreaming中的checkpoint

2624
来自专栏IT杂记

JVM垃圾收集调优案例-xwiki吞吐量调优

简介 通过压力测试查看xwiki的gc情况,统计分析gc日志,在不改变总内存使用的情况下做出合理调整,通过压力测试聚合报告对比调优效果。 步骤 运行程序,增加打...

1906
来自专栏FreeBuf

微信支付SDK 0元购Hack思路分享

* 本文作者:zjie2O71,本文属FreeBuf原创奖励计划,未经许可禁止转载

973

Cassandra的数据布局 - 调试SSTables

当您事先知道数据的格式并且可以基于过往的经验做决策时,使用Apache Cassandra处理大规模的该类型的数据是非常容易的。

2380
来自专栏CSDN技术头条

饿了么Influxdb实践之路

作者 | 刘平 文章来源GitChat,CSDN独家合作发布,查看交流实录:http://gitbook.cn/books/59428f6f7e850f039...

6086
来自专栏JetpropelledSnake

Python入门之实现简单的购物车功能

Talk is cheap,Let's do this! product_list = [ ['Iphone7 Plus', 6500], ['...

3036
来自专栏Android群英传

单应用的多进程架构

541
来自专栏FreeBuf

注意了,使用Sqlmap的你可能踩中了“蜜罐”

* 本文原创作者:九如,本文属FreeBuf原创奖励计划,未经许可禁止转载 Par0:楔子 你站在桥上看风景,看风景的人在楼上看你, 明月装饰了你的窗子,你...

1766
来自专栏前端儿

做网站,乱码?应该选用什么编码?GB2312 ? UTF-8 ?

================================================起===============================...

532

扫描关注云+社区