🌷🍁 博主猫头虎 带您 Go to New World.✨🍁
🦄 博客首页——猫头虎的博客🎐
🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺
🌊 《IDEA开发秘籍专栏》学会IDEA常用操作,工作效率翻倍~💐
🌊 《100天精通Golang(基础入门篇)》学会Golang语言,畅玩云原生,走遍大小厂~💐
🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批评指正!🍁🐥
文章目录
为什么不用 Go 编写操作系统?| 技术解析
摘要:
本文探讨了使用高级语言Go编写操作系统的可行性,以及为什么C语言在操作系统开发中占据主导地位。虽然Go具有类型安全、自动内存管理和并发等优良特性,但由于垃圾回收和运行时的限制,Go并不是最适合编写操作系统的选择。C语言以其直接内存管理、可移植性和对底层硬件的控制成为主流内核开发的首选。尽管有一些使用Go编写的操作系统研究项目,但要编写一个完整的、用户友好的操作系统仍然具有挑战性。本文还简要介绍了使用Go编写的操作系统项目Biscuit和gopher-os,以及它们所面临的局限性。
前言
操作系统是计算机系统的心脏和灵魂。操作系统管理计算机的硬件和软件资源,并为用户提供与计算机交互的手段。传统上,C语言和汇编等语言因其低开销和“接近机器”的特性而被用于编写操作系统。但是,越发流行的Go等高级语言新引入了一些特性,可以使操作系统等复杂软件更易开发。例如,类型安全、错误处理和并发等特性有利于操作系统编写。因此,Go这样的高级语言应该会成为操作系统开发的自然选择。所以,为什么不是呢?
在本文中,您将了解为什么C语言这样的语言在操作系统开发中占有重要地位,以及使用Go编写操作系统是否可行。
为什么操作系统或内核是使用C语言和汇编编写的?
用于编写特定操作系统的语言可能难以确定,因为每个操作系统都是使用多种语言的组合编写而成。
操作系统的组件具有不同职责,可能使用不同的语言编写。操作系统的核心组件是负责与硬件交互的内核,内核几乎从来都是使用C语言或汇编语言编写。面向用户的组件,例如GUI应用,可以使用任何语言编写。举例来说,Android将Java用于用户空间组件,比如GUI框架和“相机”、“电话”等系统应用。相比之下,内核使用的是C语言和汇编,而低级系统组件(例如库)使用的是C++。
本文特别关注Go是否适合编写操作系统内核。几乎所有的主流内核都使用C语言编写,中间夹杂着一些汇编。C语言统治内核领域的几个原因:
- 直接内存管理:C语言的卖点是它允许程序员直接处理内存。虽然在编写内核时手动内存管理通常具有挑战性,但您会想尽可能多地控制内存,而C语言恰恰可以满足这样的需求。内存映射I/O、DMA控制器、页表、交换等概念都需要内存操作,使用C语言可以实现。
- 没有抽象:C语言没有哈希、树或链表这样的复杂数据结构,程序员可以根据需要自己实现。这提供了对代码更精细的控制,因为程序员可以调整实现的详细信息来提高效率。
- 不需要运行时:与Java和Python不同,C语言不需要运行时。只需要一个调用main()函数的机制,C语言就能够顺畅运行。这意味着您可以直接在硬件上运行C语言程序,而不必纠结于内存管理、进程管理等问题。
- 可移植性:C语言已被移植到多种CPU架构,是编写支持多架构内核的绝佳选择。
不过,C语言本身通常不足以编写整个内核,某些情况下您需要编写汇编代码:
- 手动编写的汇编可能比编译器生成的更好。某些操作可能难以在C语言中实现,并且编译器生成的机器码可能较为复杂或效率低下。在这种情况下,手动编写汇编是更好的选择。
- 某些代码无法使用C语言编写。例如,在C语言中切换任务时,无法将寄存器保存到堆栈,也不能将堆栈指针保存到任务控制块,因为C语言不提供对堆栈指针的直接访问。
为什么Go可以成为操作系统开发的替代语言?
Go这样的高级语言提供了一些优良特性,从表面上看,它们似乎是操作系统开发的绝佳选择:
- 某些类型的bug在高级语言中不太可能出现。缓冲区溢出和use-after-free bug在Go等语言中几乎是不可能的。即使是由专业程序员精心编写的C语言代码也会无意中包含这样的bug。use-after-free bug十分常见,因此Linux内核包含内存检查器,用于在运行时检测一些use-after-free和缓冲区溢出bug。
- 在高级语言中并发更容易处理,因为几乎每种高级语言都内置了处理并发所需的机制。
- Go这样的语言的类型安全可以防止C语言的宽松类型强制。
为什么Go没有用于操作系统/内核开发?
虽然Go提供了优良的特性,可以让操作系统开发者的工作更加轻松,但它也有一些局限性。
作为具有垃圾回收的语言,Go并不真正适合操作系统开发。使用Go编写内核意味着仔细编写代码,最大程度减少堆使用来小心绕过Go的垃圾回收。如这篇Reddit帖子中所述,鼠标延迟可能是因为中断处理程序分配了触发垃
圾回收的内存。
Go还需要大量运行时才能执行,不能直接在硬件上运行。尽管TinyGo可以将Go编译为在裸机上运行,但与C语言相比,它只支持少量的架构,而C语言几乎可以在任何架构上运行。
另一个相关问题是系统调用构成了典型Go运行时中的大量操作。运行时为各种操作与操作系统通信,例如写入文件或启动线程。但是,编写操作系统时,您必须在裸机上实现这些操作,并修改Go运行时来调用实现。然后问题归结为,您是否真的想花这么多时间和精力来修改运行时,毕竟C语言等其他语言允许您立即开始编写操作系统。
正如您所看到的,使用Go编写操作系统并非不可能。不过,编写可供普通用户使用的非小型操作系统几乎不可能。编写一个在单一架构上启动并进入shell的操作系统很容易,但是使用Go编写一个在多种架构上运行、支持不同设备(如显卡或网卡)并且可能符合POSIX的操作系统将非常具有挑战性。当然一些特性可以省略,但这会限制操作系统的可行性。
使用Go编写的操作系统
虽然Go不是最适合操作系统开发的选择,但这并不意味着使用Go编写操作系统是不可能的,许多研究项目正在探索如何做到这一点。
Biscuit 是使用Go编写的操作系统,在64位X86架构上运行。它使用经过修改的Go 1.10运行时实现,其中添加了更多汇编代码来处理系统调用和中断处理程序的引导和进入/退出。使用汇编编写的引导块加载Go运行时和“shim”层。Go运行时期望与底层内核通信来提供各种功能。由于没有底层内核,“shim”层提供了这些功能。
Biscuit为用户进程提供POSIX接口,支持fork、exec等。它实现了支持核心POSIX文件系统调用的文件系统。Biscuit为使用Go编写的Intel PCI-Express以太网NIC实现了TCP/IP堆栈和驱动程序。使用POSIX接口,Biscuit可以在不修改源代码的情况下运行许多Linux C程序。
不过,Biscuit缺少许多功能,例如调度优先级、换出页面或磁盘,以及许多安全功能,例如用户、访问控制列表和地址空间随机化。
有关Biscuit的更多详细信息,请参阅白皮书。
gopher-os是另一个使用Go编写的概念验证内核。与Biscuit一样,它使用汇编来设置Go运行时和加载内核。不过,它仍处于开发的早期阶段,自2018年以来没有获得任何更新。它在最新版本的Go上也无法运作。
Clive是另一个使用Go编写的单内核操作系统,但它不在裸机上运行,并且使用经过修改的Go编译器来编译Clive软件。
gVisor是使用Go编写的应用程序内核,它在沙盒容器中实现Linux系统API。
结论
虽然C语言在操作系统开发中占主导地位,但Go提供了类型安全、自动内存管理和并发等特性,有潜力成为操作系统开发的绝佳选择。不过,由于缺乏对运行时和语言不同方面的微调控制,以及C语言的受欢迎程度,Go很难在操作系统开发领域站稳脚跟。但是,一些研究操作系统已经使用Go编写,我们可以期待在不久的将来使用Go编写用户友好型的操作系统。
参考文献:
- Biscuit: The Operating System from Scratch - https://www.usenix.org/system/files/osdi18-cutler.pdf
- Gopher-os: A Proof of Concept of an Operating System Written in Go - https://github.com/gopher-os/gopher-os
- Clive: A Single-User Operating System Written in Go - https://lsub.org/clive/
- gVisor: Application Kernel for Containers - https://github.com/google/gvisor
原创声明
======= ·
- 原创作者: 猫头虎
作者wx: [ libin9iOak ]
- 今日已学习
本文为原创文章,版权归作者所有。未经许可,禁止转载、复制或引用。
作者保证信息真实可靠,但不对准确性和完整性承担责任。
未经许可,禁止商业用途。
如有疑问或建议,请联系作者。
感谢您的支持与尊重。
点击
下方名片
,加入IT技术核心学习团队。一起探索科技的未来,共同成长。