C语言 裸骨操作系统内核编程



我最近开始对操作系统的主题感兴趣。我有几件事压在我的脑海里,但我决定分开问题。

假设我们正在为市场上的新指令集架构设计一个内核。没有 C 运行时库,什么都没有。只有该 ISA 的兼容编译器。

据推测,这意味着内核程序员唯一可用的 C 构造只是基本的赋值运算符、按位运算符和循环。这是对的吗?

如果是这样,如何在最低级别实现更复杂的事情,如主内存 I/O 和进程调度?它们只能在纯组装中实现吗?

那么,用 C(例如 Linux)编写内核意味着什么?那么内核的某些部分本质上是用汇编编写的吗?

据推测,这意味着内核程序员可以使用的唯一 C 构造只是基本的赋值运算符、按位运算符和循环。这是对的吗?

几乎所有的 C 语言功能仍然可以在您的内核中工作,而无需任何特定的运行时支持,您的 C 编译器将能够将它们转换为汇编程序,该汇编程序可以在内核模式下运行,就像在普通用户模式下一样。

但是,诸如标准 C 库之类的库将不可用,您必须编写自己的实现。特别是,这意味着在您自己实现它们之前,没有mallocfree

如果是这样,如何在最低级别实现更复杂的事情,如主内存 I/O 和进程调度?它们只能在纯组装中实现吗?

内存 I/O 是由计算机上的 CPU、BIOS 和各种其他硬件处理的低级别。值得庆幸的是,操作系统不必为此烦恼(有一些例外,例如保留了一些地址,以及一些内存管理功能)。

流程调度是一个概念,在大多数体系结构的机器代码级别并不真正存在。 x86 确实有任务和硬件任务切换的概念,但没有人使用它。这是操作系统根据需要设置的抽象,您必须自己实现它,或者如果您不想花费精力,您可以决定使用单任务操作系统,它仍然可以工作。

那么,用 C(例如 linux)编写内核意味着什么?那么内核的某些部分本质上是用汇编编写的吗?

内核的某些部分将严重依赖架构,并且必须用ASM编写。例如,在x86模式之间切换(例如,运行16位代码,或作为引导过程的一部分)或中断处理只能通过一些受保护的ASM指令来完成。您选择的架构的参考手册,如 x86 的英特尔® 64 和 IA-32 架构软件开发人员手册,是查找此类详细信息的首选。

但是 C 是一种可移植语言,它不需要这种低级架构特定的概念(尽管理论上你可以从带有编译器内部函数和内联 ASM 的 .c 文件中完成所有操作)。在汇编程序例程中将其抽象出来,并在干净的接口之上构建 C 代码会更有用,如果您想将操作系统移植到另一个架构,您可以维护该接口。

如果你对这个主题感兴趣,我强烈建议你访问操作系统开发维基,它是关于操作系统的重要信息来源,你会发现许多和你一样感兴趣的业余爱好者。

关于在汇编程序中编写代码的唯一内容是:

  • 上下文切换(将一个抽象进程的计算机状态换成另一个抽象进程)
  • 访问设备寄存器(如果设备是内存映射的,您甚至不需要它)
  • 中断处理程序的进入和退出(这是一种上下文切换)

  • 也许是引导加载程序

其他事情你应该能够在 C 代码中做。

如果你想看到这项工作做得非常好,你应该去看看Multics OS,可以追溯到60年代中期,支持大规模的信息服务(多个CPU,虚拟内存,...)。 这几乎完全是用PL/1(一种类似C的语言)编码的,只有非常小的位用支持Multics的霍尼韦尔处理器的本机汇编语言编码。 关于Multics的有机书在展示Multics如何工作以及其中大部分的清洁程度方面是值得的。 (我们得到了"太监"代替)。

无论如何,在某些地方,在汇编程序中编码是值得的。 无论编译器代码生成器的质量如何,您都可以在汇编程序中比编译器更好地手动编码某些发生在时间关键区域中的例程。 我期望这件事的地方:调度程序,系统调用进入和退出。 其他地方仅作为测量指示。 (在较旧的,小得多的系统上,人们倾向于使用大量的汇编程序编写操作系统,但这既是为了节省空间,也是为了提高效率,C编译器几乎没有那么好)。

我想知道"上市"的新架构如何还没有某种类型的操作系统。

设备驱动程序 - 有人将不得不为此编写代码,也许一个驱动程序用于 BIOS,另一个驱动程序用于操作系统。 内存映射 I/O 可能会变得复杂,具体取决于硬件,例如具有一组描述符的控制器,每个描述符都包含物理地址和长度。如果操作系统支持虚拟内存,则必须"锁定"该内存并获得物理地址才能对控制器进行编程。这是拥有一组描述符的一个原因,以便单个内存映射 I/O 可以处理已映射到连续虚拟地址空间的分散物理页。

汇编代码 - 此处的其他注释已经注意到需要一些汇编(上下文切换,中断处理程序(可以调用C函数,因此大多数代码都可以使用C))。

最新更新