Creating an Operating System

来自osdev
跳到导航 跳到搜索
难度等级
Difficulty 4.png
大师

欢迎来到操作系统开发! 本教程将记录从起步到创建新可自托管自制操作系统的过程。 这条路漫长而艰难,但有趣又有收获。 随着你逐步完成这些阶段,你将逐渐开始偏离本教程,因为你对操作系统设计会让你做出自己的决定,并且不再需要本指南。 如果你创建了一个非类Unix的操作系统,那么你将会更早出现分歧,并且必须自己填补缺失的空白。

第0阶段: 介绍

欢迎来到操作系统开发

正文: Introduction
正文: Required Knowledge
正文: Beginner Mistakes

在开始编写操作系统之前,你应该查阅所有基础文档。

构建最新的GCC

正文: Building GCC

在开始进行操作系统开发并构建交叉编译器之前,你可能希望将系统编译器升级到最新版本。

第一阶段: 开始

在此阶段,我们将建立一个工具链并创建一个基本内核,该内核将成为新操作系统的核心。

设置跨平台工具链

正文: GCC Cross Compiler

你应该做的第一件事是为自制操作系统设置一个交叉编译器。 本地系统上的编译器无法为自制操作系统生成程序,因为你的自制操作系统还未出现。 首先,你要做的是创建一种特殊编译器,该编译器负责生成将直接在目标硬件上运行的可执行文件。

创建一个Hello World内核

正文: Bare Bones
另见 其它平台上的Bare Bones

你的下一个任务是制作一个简单的hello world内核,该内核能够启动,将消息打印到输出设备,然后无休止地循环。 虽然简单而无用,但它将作为真实系统一个很好的例子和起点,并确认你的测试环境正常工作。

设立一个项目

正文: Meaty Skeleton

通过一个基本的工作示例,你的下一个任务是使用你认为合适的构建系统来设置构建基础架构。 在选择技术时要小心,GNU Make比Python更容易移植。

调用全局构造

正文: Calling Global Constructors

编译器希望你执行各种程序初始化任务,例如调用全局C++对象上的构造函数。 通常,你将使用 kernel_early_main 函数来设置最小内核功能,然后执行所有这些初始化任务,然后跳转到实际的 kernel_main 函数。

终端支持

正文: Formatted Printing

你将经常需要调试你的操作系统。 你最好的帮手是一个printf函数,它能够将字符串和整数通过终端一类的缓冲区打印到屏幕上。 尽早将printf函数添加到内核中至关重要,因为以后肯定需要它进行调试。

栈瓦解保护

正文: Stack Smashing Protector

考虑安全性和健壮性永远不嫌早。 你可以利用现代编译器提供的可选堆栈瓦解保护(Stack Smash Protector),这些编译器可以检测堆栈缓冲区溢出,而不是表现的让人出乎意料 (不幸的话,会当什么都没有发生)。

多引导

正文: Multiboot

了解引导加载程序为内核提供的功能和信息非常有用,因为这可能有助于你获取内存映射,设置视频模式,甚至内核符号表。

Global Descriptor Table

正文: Global Descriptor Table

全局描述符表是处理器状态的重要组成部分,因此它应该是初始化的第一件事之一。 即使在kernel_early之前进行设置也可能很有意义。

内存管理

正文: Memory Management

内存分配和管理是操作系统中最基本的功能之一。 你需要跟踪物理页面框架,使用的虚拟内存范围以及在其上实现堆 (malloc,free) 以供内部内核使用。

中断

正文: Interrupts

你的内核需要处理硬件发送的异步事件才能正常运行。

多线程内核

正文: Multithreaded Kernel

最好在内核开发的早期进行多线程处理,否则部分内核将最终要重写。 当我们稍后添加进程时,我们肯定会需要这个。

键盘

正文: Keyboard

你的操作系统肯定需要从计算机操作员读取输入的支持,以便它可以使其行为适应他的意愿。

内部内核调试器

正文: Internal Kernel Debugger

对于多线程内核来说,早期具有内置的调试功能非常有用。 你可以做一个魔法键,它可以停止整个内核,并将用户转储到一个带有命令行界面的迷你内核进行调试。 它可以知道调度程序用来列出所有线程并执行调用跟踪的数据结构。

文件系统支持

正文:FileSystem初始Ramdisk

尽早支持文件系统并使用初始ramdisk将文件传输到操作系统上会很有用。

第二阶段: 用户空间

在此阶段,我们将把你的操作系统扩展到用户空间,并增加对程序的支持,这足以使你的项目成为小型操作系统。 你需要处理系统调用,程序加载,内存管理以及进行针对早期部分系统的返工。

用户空间

正文: User-Space

到目前为止,你的处理器仍在内核模式下运行,所有代码可以执行所有操作。 而用户进程通常在完全没有权限的情况下运行,除了能够执行代码并使用其指定的内存。 实现用户空间的首要部分是将处理器切换到用户模式。

程序加载

正文: Dynamic Linker
正文: ELF

你需要完成的第一个任务是将程序加载到内存中。 这涉及解析程序文件头,在正确的虚拟地址处分配内存以及将段的内容复制到正确的虚拟地址。 你还需要根据重定位表填写GDT中的条目。

系统调用

正文: System Calls

你现在可以将程序加载到内存中并切换到用户模式。 进程使用系统调用与内核通信,这是你要添加的下一个功能。

操作系统特定工具链

正文: OS Specific Toolchain

由于你的操作系统现在正成为一个真正的操作系统,是时候摆正它的定位了。 我们想让交叉编译器知道有关自制操作系统及其约定的信息,这样我们就可以轻松地交叉编译程序。

创建C库

正文: Creating a C Library

此时,你可以决定使用现有的C库或编写自己的C库。 如果你采用自制,则需要设置交叉编译器为libgcc所需的一些基本功能。 有了这个,你现在可以很容易地交叉编译出程序。

分支并执行

正文: Fork

在基础程序加载到位的情况下,我们几乎可以创建一个多任务操作系统。 还缺少一些原语(primitives)允许进程创建新进程。 标准的Unix原语是fork,它允许进程创建自己的完美副本。 然后,该副本能够调用程序加载器,并用另一个程序映像的内存替换其自己的内存。

Shell

正文: Shell

这是自制操作系统中非常令人兴奋的一点。 它能够运行程序和创建新进程。 到目前为止,系统的行为可能已经在编译时确定。 通过编写一个shell,你可以运行多个程序,并决定在运行时运行哪个程序。 到此你已经达到许多新手梦想水平的等级: 具有能正常工作命令行的基本操作系统。

第三阶段: 扩展自制操作系统

有了这些基础功能,你现在就可以开始编写你的操作系统及其所有精彩功能了。 你将添加游戏,编辑器,测试程序,命令行实用工具,驱动程序以及你可以想象的任何内容。 唯一的限制是你的技能和想象力。 你可以将其中许多功能延迟到以后在自制操作系统中陆续实现,以几乎任何顺序进行。

时间

正文: Time

时间是计算中的一个复杂概念,但是现代操作系统需要具有将时间戳转换为解析时间和返回的功能,以及提供系统时钟 (实时时间,单调性时间,用户CPU时间,..) 和定时器,等这些在时钟上发生的事件。

线程

正文: Thread

操作系统应该公开线程API,比如pthreads。

线程本地存储

正文: Thread Local Storage

线程局部变量也需要运行时支持。

对称多处理(Symmetric Multiprocessing)

正文: SMP

最好在早期对你的内核添加对多个cpu的支持,否则你可能需要大量重做内核,假如它在很多地方都没有为SMP做好准备的话。

辅助存储

正文: Secondary

你可能希望支持常见的块设备,例如硬盘,cdrom,软盘以及操作系统需要支持的任何存储设备。

真实文件系统

正文: File Systems

尽早添加适当的文件系统支持是个好主意。

图形

正文: How do I set a graphics mode

真正的操作系统不是在基础文本模式下操作,而是有位图图形。 编写真正的图形驱动程序是一大堆工作,尽管一些虚拟机提供了一些有用的快捷方式。

用户界面

正文: User Interface, Compositing

你肯定想要用你华丽的图形和可用的用户界面给操作系统开发社区留下深刻印象。

网络

正文: Networking
正文: Ethernet
正文: IP
正文: TCP

网络支持的用途是显而易见的,所以你应该会想要支持这些。

声音

正文: Sound

声音是使用体验的重要组成部分,根据你的需求,你可能希望在通用硬件上支持声音。

通用串行总线(USB-Universal Serial Bus)

正文: USB

如果你需要与现代外围设备通信,则可能需要USB协议栈并支持各种通用USB控制器。

第四阶段: 自举(Bootstrapping)

你现在已经有了基础的操作系统,你已经准备好进入下一个层次。 在此阶段,我们将开始将软件移植到自制操作系统上,以便你可以自托管。 当你设置自制操作系统特定的工具链时,你已经开始努力自我托管,现在它就能给你回报。

移植软件

正文: Cross-Porting Software

虽然并非所有软件都易于移植,但大多数Unix软件都带有autoconf生成的配置脚本。 你可以为这些脚本提供 --host=mycpu-myos选项,如果自制操作系统提供了所需的功能,则可以将软件交叉编译到操作系统上。 你已经有过设置特定于操作系统的工具链时如何移植软件的经验。 虽然交叉编译软件的难度差异很大,但你将希望使用相同的过程来适应新的软件包。

移植GCC

正文: Porting GCC to your OS

当你设置操作系统特定工具链时,你已经开始了移植binutils和gcc的工作。 我们将完成该过程并将它们交叉编译到自制操作系统上,以便它可以编译C语言hello world程序。

在自制操作系统下编译你的操作系统

下一个任务是移植你的整个构建系统。 你可能需要移植GNU Make,移植一些命令行实用程序 (coreutils) 或编写自己的,移植真正的shell或完成自制shell等等。 你可能还需要修复操作系统中的许多错误,以便编译器正确运行。 你需要处理如何将新编译的版本转移到永久存储设备上,以便重新启动计算机运行又下一个版本。 你的操作系统现在配的上称为自编译(self-compiling)的。

完全自我托管

现在,你可以在自制操作系统下构建你的整个操作系统,你还需要能够完成其余的工作。 你需要能够在自制操作系统下构建你的编译器。 你需要能够在自制操作系统下进行开发,因此你将移植你最喜欢的文本编辑器或自己编写一个。 你需要联网,这样你就可以在互联网上发布最新版本 (由自制系统构建)。 你将移植大量的程序、库、游戏和任何你想要的其他东西,这样整个开发过程就可以在自制操作系统上进行。 你现在可以卸载原来的操作系统,并用新的自制亮闪闪操作系统替换它。

第五阶段: 获利

你现在已经成功创建了一个真正的操作系统,它是完全自托管的,会被整个操作系统开发社区羡慕。 你已经移植了quake,拥有OpenGL程序,可以正常工作的浏览器,蓬勃发展的贡献者社区,并取得了很大的成功。 你现在可以重新开始,从自己的操作系统开发下一个操作系统。