Bare bones OS 内核编程

Bare bones OS kernel programming

我最近开始对操作系统的话题产生兴趣。我有几件事正在考虑,但我决定拆分问题。

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

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

如果是这样,那么更复杂的事情,如主内存I/O和进程调度,是如何在最底层实现的?它们只能在纯汇编中实现吗?

那么,用 C 编写的内核意味着什么(例如 Linux)。那么内核的某些部分是否天生就是用汇编编写的?

Presumably, this means the only C constructs that are available to the kernel programmer are only basic assignment operators, bitwise operators and loops. Is this correct?

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

但是标准 C 库等库将不可用,您将必须编写自己的实现。特别是这意味着没有 mallocfree 直到你自己实现它们。

If so, how are more complex things like main memory I/O and process scheduling achieved on the lowest level? Can they only be implemented in pure assembly?

Memory I/O is something much more low level 由 CPU、BIOS 和计算机上的各种其他硬件处理。 OS 庆幸的是不必为此烦恼(有一些例外,例如某些地址被保留,以及一些内存管理功能)。

进程调度是一个在大多数体系结构的机器代码级别上并不真正存在的概念。 x86 确实有 a concept of tasks and hardware task switching but nobody uses it。这是OS根据需要设置的抽象,你得自己实现,或者如果你不想花功夫,你可以决定有一个单任务OS,它仍然有效。

What does it mean then, for a kernel to be written in C (linux for example). Are some parts of the kernel inherently written in assembly then?

内核的某些部分将严重依赖于体系结构,并且必须用 ASM 编写。例如,x86 的 on x86 switching between modes (e.g. to run 16 bit code, or as part of the boot process) or interrupt handling can only be done with some protected ASM instructions. The reference manual of your architecture of choice, such as the Intel® 64 and IA-32 Architectures Software Developer’s Manual 是查找此类详细信息的第一个位置。

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

如果您对该主题感兴趣,我强烈建议您访问 the OS Development Wiki,这是有关操作系统的重要信息来源,您会发现许多与您有共同兴趣的爱好者。

您唯一需要在汇编程序中编码的是:

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

  • 可能是引导加载程序

您应该能够用 C 代码完成的所有其他事情。

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

有一些地方无论如何都值得用汇编程序编写代码。无论您的编译器代码生成器的质量如何,与编译器相比,您将能够在汇编器中更好地手动编写出现在时间关键区域的某些例程。我认为这很重要的地方:调度程序、系统调用进入和退出。其他地方仅作为测量指示。 (在旧的、小得多的系统上,人们倾向于使用大量汇编器来编写 OS,但这对 space 的节省和执行效率一样多,C 编译器几乎没有一样好)。

我想知道 "out on the market" 的新架构怎么会没有某种类型的操作系统。

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

的分散物理页面

汇编代码——这里的其他评论已经指出需要一些汇编(上下文切换、中断处理程序(可以调用 C 函数,所以大部分代码可以在 C 中))。