动手实现一个os-2-内核结构与设计

学习自:

  • 极客时间-《操作系统45讲》
    • 购买地址: https://time.geekbang.org/column/intro/100078401
    • 作者:LMOS

上一节的HelloOS只是一个小试验/Demo,并不能真正的实现我们所需要的内核,实现我们需要的内核,那么就首先需要设计

一、黑盒中有什么

在上层用户态程序的眼中,内核就是一个黑盒,而我们往往需要手动去打开黑盒,不然只会知其然而不知其所以然

内核是硬件资源软件资源的集中式管理者,硬件资源包括:

内核对这些硬件的抽象就变成了如下的内部组成逻辑:

  • 管理 CPU:由于 CPU 是执行程序的,而内核把运行时的程序抽象成进程,所以又称为进程管理
  • 管理内存:由于程序和数据都要占用内存,内存是非常宝贵的资源,所以内核要非常小心地分配、释放内存
  • 管理硬盘:而硬盘主要存放用户数据,而内核把用户数据抽象成文件,即管理文件,文件需要合理地组织,方便用户查找和读写,所以形成了文件系统
  • 管理显卡:负责显示信息,而现在操作系统都是支持 GUI(图形用户接口)的,管理显 卡自然而然地就成了内核中的图形系统
  • 管理网卡:网卡主要完成网络通信,网络通信需要各种通信协议,最后在内核中就形成了网络协议栈,又称网络组件
  • 管理各种 I/O 设备:我们经常把键盘、鼠标、打印机、显示器等统称为 I/O(输入输出)设备,在内核中抽象成 I/O 管理器。

内核除了这些必要组件之外,根据功能不同还有安全组件等,最值得一提的是,各种计算机硬件的性能不同,硬件型号不同,硬件种类不同,硬件厂商不同,内核要想管理和控制这些硬件就要编写对应的代码,通常这样的代码我们称之为驱动程序,硬件厂商就可以根据自己不同的硬件编写不同的驱动,加入到内核之中

但是光有组件还不行,还需要对这些组件进行高效的管理,如何设设计此部分,那么就需要了解目前两种经典的内核设计结构(其实这两种在《深入理解linux内核》中的绪论已经浅显的学习过了,见《深入理解linux内核》-1-绪论

二、内核设计结构

单块结构/单体结构/宏内核结构

把所有内核功能或者说每个内核层都集成到一个内核程序中,并且在内核态下运行具有唯一地址空间(大多数Unix系统)

这个大程序里有实现支持这些功能的所有代码,向用户应用软件提供一些接口,这些接口就是常说的系统 API 函数

图中虽然是一层层的,但是并没有上下层依赖的关系,仅仅表示它们链接在一起

例子:宏内核提供内存分配功能的大致过程:

  1. 应用程序调用内存分配的 API(应用程序接口)函数

  2. 处理器切换到特权模式,开始运行内核代码

  3. 内核里的内存管理代码按照特定的内存分配算法,分配一块内存

  4. 把分配的内存块的首地址,返回给内存分配的 API 函数

  5. 内存分配的 API 函数返回,处理器开始运行用户模式下的应用程序,应用程序就得到了 一块内存的首地址,并且可以使用这块内存了

宏内核结构的主要缺点在于:高度耦合化,拓展型差,也没有模块化,依赖性高,一旦一个地方出了问题,所有其他部分都可能会出问题并且要重新编译安装

唯一的优点在于就是性能较好,因为组件之间可以直接的互相调用

微内核

只需要内核有一个很小的函数集(同步原语、调度程序、进程间通信机制等),几个在其上运行的系统进程代替操作系统的功能(大多数操作系统学术研究的都是此架构),是一种分层的结构,上层使用下层的服务,其中操作系统层是一个独立的服务程序

微内核的架构是不能完成什么实际功能的,所以开发者们把实际的进程管理、内存管理、设备管理、文件管理等服务功能,做成一个个服务进程,宏内核提供的功能,在微内核架构里由这些服务进程专门负责完成

微内核定义了一种良好的进程间通信的机制——消息。应用程序要请求相关服务,就向微内核发送一条与此服务对应的消息,微内核再把这条消息转发给相关的服务进程,接着服务进程会完成相关的服务

所以服务进程的编程模型就是循环的处理来自其他进程的消息,然后完成相关的服务功能

同理,微内核的一个例子如下:

  1. 应用程序发送内存分配的消息,这个发送消息的函数是微内核提供的,相当于系统API,微内核的API(应用程序接口)相当少,极端情况下仅需要两个,一个接收消息的API和一个发送消息的API
  2. 处理器切换到特权模式,开始运行内核代码
  3. 微内核代码让当前进程停止运行,并根据消息包中的数据,确定消息发送给谁,分配内存的消息当然是发送给内存管理服务进程
  4. 内存管理服务进程收到消息,分配一块内存
  5. 内存管理服务进程,也会通过消息的形式返回分配内存块的地址给内核,然后继续等待下一条消息
  6. 微内核把包含内存块地址的消息返回给发送内存分配消息的应用程序
  7. 处理器开始运行用户模式下的应用程序,应用程序就得到了一块内存的首地址,并且可以使用这块内存了

可以明显看出,微内核的主要问题就是在于消息通信的开销较大,所以系统调用涉及的系统进程较多,那么整个过程会变得更加慢

当然,优点也非常明显:更好的拓展型、解构模块化利于开发、利于移植等等

MACH、MINIX、L4 系统都是微内核的代表作,但是商业级别的系统都不会使用微内核,主要还是因为性能较差

==> linux是如何取舍的呢?

  • linux采用了动态链接模块的思路,取长补短,将每个功能设计为单独的模块,这些模块不是系统进程而是静态内核函数/代码,同样的实现了这些进程的功能,同时这些模块也不是一开始运行就全部加载,而是可以在运行的过程中加载与解除链接,这就是Linux的混合内核

三、分离硬件相关性

计算机领域的一个基本方法是增加一个抽象层(无论是网络、内核、还是软件开发),从而使得抽象层的上下两层独立地发展,所以在内核内部再分若干层也不足为怪

分离硬件的相关性,就是要把操作硬件和处理硬件功能差异的代码抽离出来,形成一个独立的软件抽象层,对外提供相应的接口,方便上层开发

以进程切换为例:

为什么要进程切换?

说到底其实就是内核/操作系统要实现多任务的假象,即每个进程只运行一小段时间,然后迅速的切换到另一个进程,这样的过程对于用户/人类来说难以感知,并且在当前多核处理器的天下,每个CPU都能实现这样的多任务,也就大大提高了效率

进程切换的核心机制:

  • 进程调度算法:下一个进程选择哪一个切换更高效?
  • 进程停止与继续:如何保存当前进程的上下文从而之后继续运行?

那么这两种机制其实不管底层硬件是什么平台,选择一个进程的调度算法是可以多样的,并且不会频繁改变的,因为与底层硬件无关

但是进程的停止与继续或者说进程的切换这是与底层的硬件强相关的,因为要保存进程的上下文,不同硬件的上下文结构是不同的

所以,设计上就要将进程的切换放在一个独立的层中实现,比如硬件平台相关层,这样不同平台不同的实现,那么移植性就大大增强了,这就是分层设计的好处

如果把所有硬件平台相关的代码,都抽离出来,放在一个独立硬件相关层中实现并且定义好相关的调用接口,再在这个层之上开发内核的其它功能代码,就会方便得多,结构也会清晰很多

四、我们的实现

虽然前面的内容,对操作系统设计这个领域还远远不够,但是对于我们自己从零开始的操作系统内核这已经够了

我们将操作系统大致分为三个大层:

  • 内核接口层
    • 定义了一套Unix接口的子集(因为学习的目的,所以接口很少,这几个接口能大致定义出操作系统的功能即可)
    • 检查调用参数合法、返回错误等,合法的调用继续向下调用
  • 内核功能层:主要就是完成实际的功能,这些功能又会分为各种模块,具体模块如下
    • 进程管理,主要是实现进程的创建、销毁、调度进程,当然这要设计几套数据结构用于表示进程和组织进程,还要实现一个简单的进程调度算法
    • 内存管理,在内核功能层中只有内存池管理,分两种内存池:页面内存池和任意大小的内存池,你现在可能不明白什么是内存池,这里先有个印象就行,后面课程研究它的时候 再详细介绍
    • 中断管理,这个在内核功能层中非常简单:就是把一个中断回调函数安插到相关的数据结构中,一旦发生相关的中断就会调用这个函数
    • 设备管理,这个是最难的,需要用一系列的数据结构表示驱动程序模块、驱动程序本身、驱动程序创建的设备,最后把它们组织在一起,还要实现创建设备、销毁设备、访问设备的代码,这些代码最终会调用设备驱动程序,达到操作设备的目的
  • 内核硬件层:主要包括一个具体硬件平台相关的代码
    • 初始化,初始化代码是内核被加载到内存中最先需要运行的代码,例如初始化少量的设备、CPU、内存、中断的控制、内核用于管理的数据结构等
    • CPU 控制,提供 CPU 模式设定、开、关中断、读写 CPU 特定寄存器等功能的代码
    • 中断处理,保存中断时机器的上下文,调用中断回调函数,操作中断控制器等
    • 物理内存管理,提供分配、释放大块内存,内存空间映射,操作 MMU、Cache 等
    • 平台其它相关的功能,有些硬件平台上有些特殊的功能,需要额外处理一下

可以发现,我们的系统设计上没有任何设备驱动程序、文件系统和网络组件,所以我们把文件系统、网络组件、其它功能组件作为虚拟设备交由设备管理,比如需要文件系统时就写一个文件系统虚拟设备的驱动,完成文件系统的功能,需要网络时就开发一个网络虚拟设备的驱动,完成网络功能,这些驱动一旦被装载,就是内核的一部分了,并不是像微内核一样作为服务进程运行(这不就是Linux的设计思路吗;))

五、Linux全景图

https://makelinux.github.io/kernel/map/

image-20220719153039173

此图并不是Linux内核的全部,但几乎包含了其核心

可以大致分为五个组件:

image-20220719153318071

Linux 这么多模块挤在一起,之间的通信 主要是函数调用,而且函数间的调用没有一定的层次关系,更加没有左右边界的限定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xxx_undefined

谢谢请博主吃糖

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值