操作系统 (OS) 是计算机系统的心脏和灵魂,它管理着计算机的硬件和软件资源,并为用户提供与计算机交互的方式。传统上,C 和 Assembly 等语言因其低开销和 “接近机器码” 的特性而被用于开发操作系统。
但诸如 Go 等高级语言的兴起引入了一些特性,这些特性或许可以使开发操作系统等复杂软件变得更加容易。例如,类型安全、错误处理和并发性在开发操作系统时应该是有益的。
因此,采用像 Go 这样的高级语言来开发操作系统理应是自然选择,但为什么并没有成功的案例?
操作系统由不同的组件构成,它们负责不同的功能,可以使用不同的编程语言编写。
操作系统的核心是内核 (Kernel),它负责与硬件交互 —— 几乎都是采用 C 或汇编语言编写。至于面向用户的组件(例如 GUI 应用程序),可以采用任何语言编写。
例如,Android 采用 Java 编写用户层的组件,如 GUI 框架和系统应用程序(相机、电话等)。相对应的,其内核采用 C 和汇编语言编写;底层系统组件 —— 库,则是用 C++ 编写。
C 语言 “统治” 内核的主要原因:
-
可直接管理内存
-
缺少抽象
-
无需依赖运行时
-
可移植性
Go 作为高级语言提供了许多令人满意的特性。从这方面来看,它似乎可以成为开发操作系统的绝佳选择:
-
某些类型的错误在高级语言中出现的可能性要小得多
-
并发性:在高级语言中处理并发更容易,因为几乎每种高级语言都内置处理并发所需的机制
-
类型安全:防止像 C 的宽松类型强制执行
不过尽管 Go 提供了令人满意的特性,可以让操作系统开发者的工作更轻松,但它也有一些局限性。
作为一种具有垃圾回收功能的语言,Go 并不真正适合操作系统开发。使用 Go 编写内核意味着需要小心翼翼地绕过 Go 的垃圾收集。正如 Reddit 论坛中开发者提到的,鼠标滞后可能是因为中断处理程序分配了触发垃圾回收的内存。
此外,Go 还需要大量的 runtime 才能执行,这意味着它不能直接在硬件上运行。尽管 TinyGo 可以将 Go 编译为在裸机上运行,但与 C 相比,它只支持少量的架构,而 C 几乎可以在任何架构上运行。
总的来说,使用 Go 开发一个不是 “玩具性质” 的操作系统几乎是不可能。尤其是要求支持在多种架构上运行、支持不同设备(如显卡或网卡),并符合 POSIX 标准,这更是极具挑战性。
最后看一下探索用 Go 开发操作系统的案例。
Biscuit 是一个用 Go 开发的操作系统,运行在 64 位 X86 架构上。它使用经过修改的 Go 1.10 运行时实现,其中添加了更多汇编代码来处理系统调用和中断处理程序的引导和进入 / 退出。
Biscuit 为用户进程提供 POSIX 接口,支持 fork、exec 等。它实现了支持核心 POSIX 文件系统调用的文件系统。Biscuit 为用 Go 编写的 Intel PCI-Express 以太网 NIC 实现了 TCP/IP 堆栈和驱动程序。使用 POSIX 接口,Biscuit 可以在不修改源代码的情况下运行许多 Linux C 程序。
然而,Biscuit 缺少许多功能,例如调度优先级、换出页面或磁盘,以及安全功能,例如用户、访问控制列表和地址空间随机化。
欢迎点赞收藏转发🙏🙏
---- END ----