Go语言编写操作系统内核一直是一个冷门的领域,难以想一个带runtime的语言如何在没有操作系统的裸机上启动运行,让语言自己的功能能正常运行都难,更别说做操作系统常见的内存管理,进程管理等。但也有人不断地尝试,比如以下几个项目:

1.tinny

https://github.com/golang/go/commit/434f1e32a075d546f47943598aa5974d4a2492ce​github.com

这个是Go官方的早期版本的一个实现,能跑concurrent prime sieve,现在已经删掉了,可以看到在Go早期版本里面runtime还没有那么复杂的时候,移植到裸机其实挺简单的,想考古的同学可以试一下,里面用到的bootloader是著名的xv6的实现,因为Russ Cox自己就是xv6的作者之一。

2.gopher-os

https://github.com/gopher-os/gopher-os​github.com

这个通过修改go的编译过程以及重定向runtime符号实现了一个概念内核,项目本证明了go能脱离操作系统运行在裸机上,最终的效果是在图形化终端上打印了一下硬件信息,没有过多的其他应用。

3.biscuit

mit-pdos/biscuit​github.com

这是一个博士论文项目,完成度很高,多核,多线程,网络协议栈等等。这个项目是奔着写一个操作系统内核去的,其兼容了一些POSIX接口,根据论文里面的内容,可以跑redis和nginx。实现方式是hack了linux的amd64 arch相关代码,注入了自己的实现,代码整体分为两部分,一部分存在于go的标准库里面的runtime目录下,一部分是在项目的biscuit目录下。

我最早的想法也是借鉴这个项目,不过不是hack runtime的某个arch和os,而是新造了一个GOOS=eggos,编译的时候指定这个GOOS环境变量就能生成相应的内核,不过为了能让工具链和标准库能编译通过,修改了很多标准库代码,大部分只是加了一个编译tag,比如这个文件的这行代码,就需要在开头加上我自己的操作系统名称,类似这样的文件很多,这样做其实是不利于跟进go的版本升级的。

golang/go​github.com

4.unik

~eliasnaur/unik - sourcehut git​git.sr.ht

这个项目是我在找Go实现的平台无关的GUI项目时发现的,作者写了一个Go的GUI项目,同时为了证明其跨平台能力用Go写了一个运行于裸机的应用程序,显卡驱动使用了virtio。作者还是挺牛的。我后来又深挖了一下,发现他之前就在Go的issue提过想让官方提供一个GOOS=none的实现,issue里的很多资料还是很有用的,具体讨论见这里

https://github.com/golang/go/issues/35956​github.com

这个项目对Go runtime的启动方式对我启发很大,在看到这个项目后,我把我之前的实现改成类似的方式,之后就摆脱了对Go runtime代码库的依赖。

总结一下几种实现裸机上运行Go的方式:

  1. 直接加一个新的GOOS,GOARCH看自己要实现的平台,这种方式需要修改runtime,编译器以及标准库的代码,优点是实现比较直接,并且可以做一些定制,想更改runtime行为的时候代码不绕弯,缺点是与特定Go版本的代码绑定,不利于后续升级。
  2. 魔改Go的编译链接过程,将runtime里面的os相关实现链接到自己的实现上,本质上跟第一种方案类似,但没有修改runtime代码,缺点是Makefile太复杂,并且很依赖特定版本的编译器。
  3. 更改最终生成的ELF程序的入口,引导到自己实现的初始化代码,最后再跳转到Go的runtime入口。这种方案是目前我觉得最简洁的方式,即不用修改runtime代码,又用的是标准工具的能力,之后兼容更高的Go版本也不会有大改。

当然让Go程序在裸机上跑起来远不止上面说的那些,上面说的只是解决了runtime的bootstrap的问题,但让Go程序使用基本的new, interface,goroutine,channel等,我们至少还要解决以下几个问题:

  1. 内存,Go是一个带内存管理的语言,内存问题是我们首要解决的,不然就回到手工管理内存的时代。好消息是Go的runtime对OS的内存接口依赖很挺简单,主要有sysAlloc,sysReserve,sysMap等,如果采用第一种方式实现内核,可以参考wasm(runtime/mem_js.go)的实现,由于wasm整个是一个线性内存,跟裸机的内存模型一致,因此很有参考性。如果是后面几种实现,就要模拟linux的mmap系统调用,这个后面再说。
  2. 线程,线程对应Go GMP模型里面的M,用于执行某个Goroutine的代码。如果是第一种实现,可以参考wasm在runtime里面的代码,wasm是单线程实现,因此不用考虑多个M的情况,结果是wasm里面没有启动sysmon协程,纯粹靠编译器打桩的代码来实现协程调度。如果是后面几种实现,就要模拟linux的clone系统调用,这个后面再说。
  3. 锁,这里的锁特指runtime里面使用的mutex和note两种很重要的同步设施,主要用于同步多个M的。但在wasm里面因为只有一个线程,因此wasm的线程相关实现几乎是空操作(除了notesleep),但Linux里面就用的是实实在在的futex实现的。还是看我们的实现,第一种方式就可以参考wasm的实现(runtime/lock_js.go),如果是如果是后面几种实现,就要模拟linux的futex系统调用。

实现了上面几个基本功能之后,就能写一些纯内存操作的Go程序了,goroutine,channel啥的随便用,但要想使用标准库里面的文件,网络等功能还是需要后续的两个重要工作,中断和系统调用

平时我们写Go用户应用程序的时候是不关注中断和系统调用这些概念的,因为Go的标准库帮我们封装了这些事情。比如我们写一个网络程序,在Go里面直接调用net.Conn.Read阻塞等待数据返回就可以了,在没有数据的时候,go的runtime挂起当前goroutine,同时在runtime内部使用了epoll_wait系统调用来等待fd的可读事件。在操作系统层面,收到epoll wait调用后,操作系统挂起当前等待的线程,调度其他进程。等网卡收到了数据包的时候,触发了操作系统的网络中断例程,经过操作系统的网络栈处理原始数据包之后,操作系统又唤醒了epoll wait线程,告诉它哪些fd可读,Go runtime这个时候再唤醒调用Read的goroutine来读取数据。

上面的整个过程对普通用户是透明的,但我们要跑在裸机上,上面的应用程序到硬件之间的Gap就需要我们亲自去填补了。关于如何处理中断和系统调用本身就可以另外写一篇文章了,这里简单说一下遇到的难点。

前面说过,Go除了sysmon协程辅助调度之外,在函数入口以及系统调用的地方加了一些指令来检查栈是否溢出,以及是否应该调度当前协程等。这些在用户程序里面没有问题,但在内核代码里面,这些就很致命了,这些检查代码一方面会假定当前有可用的G或者P,另一方面会发生潜在的协程调度,而在中断或者系统调用代码里面,是不能假定有可用的G或者P的,也不能发生调度,如何处理好这些问题真的很难。好消息是,Go runtime也会遇到这些问题,因此我们有现成的代码可借鉴,但坏消息是,写这些代码真的是如履薄冰,任何高级的Go 特性都不能使用,一点都不欢乐,因此大家看到我的这个项目里面晦涩代码的时候,其实当时写这些代码的人也很无奈。然而还有另外一个好消息,我把这些问题通过类似微内核的思想给解决了,具体可以看一下我的这个Go项目的代码。

说到这里就给大家介绍一下我写的这个项目。最早我萌生编写将Go跑在裸机上的项目是觉得Go的runtime做了很多类似操作系统的事情,比如goroutine对应进程,channel对应进程间通信,Go还有完善的内存管理,关键是Go编译后是一个二进制,不需要虚拟机。因为我上学的时候用C写过一个miniOS,觉得Go可能做一个简单的适配层就可以跑在裸机上了,说干就干,疫情期间有很多时间让我去思考和实验这个想法,最后还真成功了。

https://github.com/icexin/eggos​github.com

给大家看几个作品的快照

Javascript引擎里面执行HTTP GET

NES模拟器里面跑马里奥

目前实现的功能有:

  • 基本的Go功能,goroutine,channel,GC等。
  • 一个带自动补全,历史查找功能的console。
  • TCP/IP网络协议栈,进而支持一些基本的网络程序,如sshd,httpd等
  • Go风格的VFS抽象,附带了一个smbfs实现。
  • NES模拟器
  • Javascript解释器
  • ....

当然很多功能是通过支持完Go的标准库后,直接import第三方库实现的,但这也侧面证明了对Go语言自身的实现程度。

后续计划加入这些功能:

  • 对amd64的支持
  • 完善的浮点数支持,从而能支持软3D渲染,不得不说fogleman真是大神,仓库里面有很多宝藏。
  • 鼠标和GUI支持
  • SMD多核支持

好了,文章就写到这,希望对探索Go runtime的Gopher以及想编写操作系统的小伙伴有帮助,如果大家对项目有什么疑问或者发现bug,都可以去github提issue。

进程调度程序模拟_将Go程序跑在裸机上相关推荐

  1. 进程调度程序模拟_通用设计:指挥调度系统——复杂行动的大脑与神经

    指挥与调度系统能大幅提高行动的整体性,协调性,并能根据提前推演出的预案解决实际行动时决策困难的问题.本篇文章分享指挥调度系统的设计方案与模型处理步骤,带领我们更具体地了解指挥调度系统的组成设计. 背景 ...

  2. ubuntu虚拟机进程被杀死_服务器上程序显示进程被杀死

    作为一个javaer,我以前写过很多关于Linux的文章.但经过多年的观察,发现其实对于大部分人,有些东西压根就用不着.用的最多的,就是到线上排查个问题而已,这让人很是苦恼.那么,我们就将范围再缩小一 ...

  3. windows制作docker镜像_.NET Core程序跑在任何有docker的地方

    (给DotNet加星标,提升.Net技能) 转自:沛山cnblogs.com/peyshine/p/12918315.html 一.分别在Windows/Mac/Centos上安装Docker Win ...

  4. 使用动态优先权的进程调度算法的模拟_我爱OS第12讲:系统调度

    操作系统必须为多个进程分配计算机资源.对于处理机而言,可分配的资源是在处理机上的执行时间.处理机是计算机系统中的重要资源,处理机调度算法不仅对处理机的利用率和用户进程的执行有影响,同时还与内存等其他资 ...

  5. 2020年上海市高等学校信息技术水平考试试卷_三级_数据科学技术及应用_模拟卷_三、程序填空题_答案

    2020年上海市高等学校信息技术水平考试试卷_三级_数据科学技术及应用_模拟卷_三.程序填空题_答案 (本试卷考试时间 150 分钟) 答案是自己做的,经验证,可成功运行. 内容仅供学习交流,不可转载 ...

  6. 多级队列调度算法可视化界面_进程调度功能由操作系统内核的进程调度程序完成...

    进程调度的功能与时机 一.进程调度的功能 进程调度功能由操作系统内核的进程调度程序完成,在Linux 内核中,进程调度功能的实现从调用内核函数schedule()开始.进程调度的功能是按照某种策略和算 ...

  7. c语言与64位windows不兼容_微软发布可模拟 64 位 x86 程序的 ARM 版 Windows 10

    微软今天宣布推出可以在 ARM 架构 PC 上模拟 64 位 x86 程序的新版 Windows 10.这意味着,拥有 ARM PC 的用户,比如 Surface Pro X 可以安装 64 位 x8 ...

  8. 单片机长时间程序跑飞_单片机程序跑飞的三种现象、原因及解决方法

    今天在编写单片机程序的时候,由于中断服务程序写的不好,导致单片机程序总是跑飞,最后费了好长时间,花了很大功夫才找到问题原因,由此总结了单片机程序跑飞的三种现象.原因及解决方法. 一.数组越界(数组溢出 ...

  9. java 进程跑飞_RK3288 st7703 mipi屏指令过长,程序跑飞

    CPU:RK3288 系统:Android 5.1 调试 mipi 屏前,先关闭了 uboot 的 logo 显示 rockchip,uboot-logo-on = <0>; 屏调试完成后 ...

最新文章

  1. ios技术篇-CoreData
  2. wait和notify
  3. Node安装node-sass总是下载超时问题解决
  4. 台积电获苹果A10大量订单 三季度销售额将增长20%
  5. 近世代数--素理想--I是R的素理想↔R/I是整环
  6. to_string()函数----将int类型转换为string型
  7. 第2章 信号、接口和引脚(XIlinx ZYNQ-7000 SOC UG-585文档)
  8. 采用光线跟踪绘制场景 c++_虚拟演播室的跟踪系统以及色键器应要选择什么型号...
  9. 最长回文子序列与最长回文子串
  10. 多linux服务器之间实现文件自动复制(脚本)
  11. 【Redis】《Redis 开发与运维》笔记-Chapter10-集群
  12. php蝠衭厍桴埭钨,有没办法判断输入汉字的笔划数?
  13. 2023java面试看完这篇笔记薪资和offer稳了!
  14. [开发浏览器实战]关于Firefox火狐浏览器的说明一二(国内版 国际版区别 账号切换 插件-恢复关闭的标签页 插件-tempermonkey油猴)
  15. Unity 【Content Size Fitter】- 聊天气泡自动适配Text文本框大小
  16. python微信群聊机器人_Python + itchat 实现微信机器人聊天(支持自动回复指定群聊)...
  17. 进程三态与五态是什么?
  18. 单价数量和总价的公式_知道总价和数量怎么算单价
  19. Java第十天笔记01——文件与流
  20. 【raid5数据恢复】服务器RAID5中一块硬盘亮黄灯被踢出导致raid崩溃的数据恢复

热门文章

  1. 你不清楚的18个非技术面试题是这些
  2. CnOpenData中国专利详细地址数据
  3. 申请https证书要多久
  4. 程序人生 - 数字人民币与微信支付宝有何不同?
  5. 【编程开发】之短信注册用户流程
  6. 2021-11-15其他进制之间的转换
  7. php decrypt,GitHub - qiling/php-decrypt: PHP Decrypt是一个跨平台用来解密PHP源码的扩展
  8. 抽象代数之12阶群的五种结构
  9. PMP模拟试题与解析(八)
  10. 高仿可伸缩小米日历 支持添加自定义提示数据