模块化内核与微内核/单片内核

Posted

技术标签:

【中文标题】模块化内核与微内核/单片内核【英文标题】:modular kernel vs micro kernel / monolitic kernel 【发布时间】:2018-03-06 18:12:06 【问题描述】:

我是 C 程序员,是 Linux 内核编程的新手。我可以发现有 3 种类型的内核单片,微型和模块化内核。在谷歌搜索时,我可以找到一些网站说 linux 具有单片内核(在堆栈溢出中),而另一些则说微内核,其余的说混合内核。所以我在阅读模块化概念时完全感到困惑,它说可以在不重新编译内核的情况下添加驱动程序的新模块,这与我认为 Linux 使用单片内核的假设背道而驰。单片内核在单个地址空间中运行,并且作为一个单个进程,如果是这样的话,这也有点令人困惑

【问题讨论】:

它是一个大型的单片内核。将其与 Plan 9 或 Mach 进行比较,以更好地了解差异。 【参考方案1】:

在尝试理解这些差异之前,您必须先了解其他概念:

1。模块化编程。

模块是程序功能完整的部分。模块通常具有以下属性:

    分离接口实现初始化去初始化例程。两者都是可选的。带有 GC(垃圾收集器)的环境中可能缺少取消初始化例程。 程序使用的模块组成有向无环图,也称为依赖图(您可能听说过 - 不允许循环依赖,依赖在依赖模块之前初始化)。

在构建大型系统时,模块化编程是必不可少的。每个大内核都是一个模块化内核,无论它是单体内核、混合内核还是微内核。

有时模块可以动态加载和卸载。动态模块是任何可扩展系统的重要组成部分。这些可以是插件,或者,如果我们谈论内核,则可以是独立于内核开发和分发的驱动程序。

2。安全和不安全的语言。

安全语言非常严格地定义程序中可能发生的事情。最重要的是,他们没有malformed 程序(或无意义 程序)的概念。每个程序都是有效的,它的执行总是遵循语言规范。程序是否按照程序员期望的那样做,在这种情况下是无关紧要的。

安全语言的共同特征:

    他们使用垃圾收集。 它们没有指针算法。这意味着不允许写入或读取任意地址。 它们防止超出范围的数组访问(如果有这样的概念)。异常或类似机制可用于发出信号并从此类故障中恢复。 引用(或指针)只有两种可能的状态:空引用和对有效对象的引用。这是由垃圾收集器保证的。事实上,GC 是这里的关键组件。有些语言走得更远,根本不允许空引用。 每个对象(正在使用的内存块)都有分配给它的类型信息,对象只能通过适当类型的引用来访问,例如您不能通过引用字符串来访问整数数组。

您可以在此列表中添加更多条目,但基本思想是保证程序只能使用有效操作访问有效内存区域。请记住,一些不安全的语言可能会共享部分甚至全部这些特征。

安全语言示例:Python、Java、C# 的安全子集。

不安全的语言定义了程序中可以做什么和不可以做什么,但通常没有什么可以阻止程序员做错事。违反这些规则的程序称为格式错误的程序。从语言的角度来看,这样的程序是没有意义的,语言甚至没有尝试定义它的行为,因为它通常几乎不可能做到。就 C 而言,此类程序的行为是 undefined

不安全语言的示例:汇编程序、C、C++、Pascal。

3。硬件不安全,因此必须使用不安全的语言进行编程。

大多数硬件无法为您提供安全的环境。有一些处理器用于将类型信息附加到每个内存单元(请参阅 tagged architecture),但现代处理器不会这样做,因为它会使硬件复杂化,使其速度更慢、更昂贵且通用性更低。

仍然提供了一些功能,以便在不安全的硬件环境中实现安全环境,例如内存保护、单独的地址空间以及 用户模式 上的执行模式分离内核模式(又名主管模式)。

内核是在裸机上运行的,因此其中大部分必须用 C 和汇编等不安全的语言编写。另一个原因是性能 - 安全的环境意味着巨大的开销。

微内核和单体内核

单片内核及其模块在单个共享地址空间中运行。而且由于所有内容通常都是用不安全的语言编写的,因此内核的任何部分都可能由于代码中的错误而访问(并损坏)属于内核另一部分的内存。这种环境的不安全性质使得无法检测到这些故障或从这些故障中恢复,最重要的是无法预测此类故障后的内核行为。

微内核试图通过将内核的各个部分移动到单独的地址空间来克服这些限制,有效地将它们彼此隔离,但提供相互通信的安全方式(通常通过消息传递)。这种分离创建了由多个不安全进程组成的安全环境,允许内核从其某些子系统的故障中恢复。

同时,单片内核可以在单独的地址空间 (FUSE) 中运行它的一部分,而没有什么能阻止微内核能够支持与内核的主要部分共享地址空间的模块。

如果大部分内核运行在一个单一的地址空间,它被认为是一个单片内核。如果其中大部分运行在单独的地址空间中,则此类内核被认为是微内核。如果内核介于两者之间并积极使用这两种方法,那么您就有了一个混合内核

混合内核

混合内核的概念意味着将两全其美的结合在一起,由微软在 90 年代为促进 Windows NT 的销售而发明。玩笑!但这几乎是真的。 Windows NT 的每个重要部分都在共享地址空间中运行,因此它是单体设计的另一个例子。

在描述能够动态加载模块的单片内核时,许多人似乎都使用了这个术语。这是因为过去单片内核不支持动态模块加载,并且每次将模块添加到内核时都必须重新编译。微内核不是关于动态模块加载,而是关于内核的可靠性,关于它从子系统故障中恢复的能力。


答案:Linux 是一个单体内核。


单片内核可以是模块化的并且可以动态加载模块。另一方面,微内核必须是模块化的,必须能够动态加载模块——整个想法是在单独的地址空间中运行它们。 p>


微内核并不是克服单体内核不安全特性的唯一方法。另一种方法是用安全的语言编写单片内核。这种方法的一个问题是安全环境应该由硬件提供(并且将非常有限),或者应该使用不安全的语言在软件中实现。这种环境的实现将非常复杂,并且很可能会有很多错误(想想在 JVM 中发现的所有错误)。

这方面的示例是实验性操作系统Singularity。

【讨论】:

【参考方案2】:

好吧,考虑到我明天可能会有一个小测验,我应该可以帮助你。不过,我还在学习中,虽然我的帖子可能存在一些技术错误,但在概念上应该是合理的

基本上,您可能会理解,操作系统有不同类型的内核。

单片内核将所有系统功能和服务集中在一个巨大的程序中,占用一个地址空间。 另一方面,微内核在微内核上拥有最少的系统程序和服务。大多数以前被认为是内核的一部分(在单片内核版本期间)的服务,例如进程调度程序等,现在都在用户空间中,被称为服务器。这些服务器通过微内核相互通信,使用进程间通信,这是一种由微内核制定的通信形式。 模块化方法以此为基础,通过使这些“服务器”可动态加载。因此,可以动态加载特定的“服务器”(在这种类型的内核中,称为模块),而内核不需要重新编译自身。

Linux 内核是单片内核,但大多数 Linux 风格(如 Ubuntu、Solaris)使用混合内核,即混合了单片内核和模块化内核方法。这很常见,不同的内核结构各有优劣,需要混合结构来平衡

【讨论】:

是的,我从上面的解释中得到了一些突破。正如您所说,单片内核占用单个地址空间,因此如果用户进程正在运行,它将共享单片地址空间还是为该用户进程单独的地址空间,与单片单地址空间完全不同 据我了解,所有情况下的用户地址空间都是相同的。造成这种情况的一个主要原因是,一个进程可能会使用一个模块一次,而在不同的时间使用另一个模块。这些模块将位于不同的地址空间。此外,考虑到 Linux 操作系统(例如 Ubuntu)使用混合模块化单片内核(其中内核是可以加载到内核的模块的一件大事),具有不同的地址空间没有多大意义。【参考方案3】:

有关您的问题的一些信息,请参阅this prior *** question。简而言之,听起来您很想知道...

...阅读模块化概念,它说可以在不重新编译内核的情况下添加驱动程序的新模块,这与我认为 Linux 使用单片内核的假设背道而驰。单片内核在单个地址空间中运行并作为单个进程...

这两个概念(“模块化内核”和“单一地址空间”)实际上并不矛盾。您可以构建一个新的内核模块,而无需重新编译整个 Linux 内核。当您加载这个新的内核模块时,它实际上会被加载到与正在运行的内核本身相同的地址空间。从上面的链接...

不要将术语模块化内核与单体内核混淆。一些单片内核可以编译为模块化(例如 Linux),重要的是模块插入到处理核心功能的同一空间(内核空间)并从其运行。

正如您所发现的,有几种方法可以对内核进行分类,并且不同的类型不一定是相互排斥的。

【讨论】:

以上是关于模块化内核与微内核/单片内核的主要内容,如果未能解决你的问题,请参考以下文章

(笔记)Linux内核学习之内核介绍

弄好的内核模块怎么弄进开发板里面去啊?(我已经用交叉编译器德到了.ko文件)

宏内核与微内核

Linux 内核宏内核与微内核架构 ( 操作系统需要满足的要素 | 宏内核 | 微内核 | Linux 内核动态加载机制 )

驱动编译进内核和编译模块的区别

内核模块和内核参数