我这人不懂什么操作系统,于是用Go语言模拟出了一个

本文介绍了作者如何用Go语言模拟出一个名为Sham的操作系统,它包含标准输入输出、进程间通信和时间片轮转调度。Sham的代码简洁,注释详细,适合学习操作系统原理。文章详细讲解了Sham的系统结构、工作流程、线程运行、中断与IO,以及如何设计和运行Sham程序。
摘要由CSDN通过智能技术生成

Introducing: Sham

1024那天写了篇的文章《Python 代码一键转流程图》。CSDN 居然给了个 “最趣味”奖🏆:https://blogdev.blog.csdn.net/article/details/109536460 。

一开始我是不知道获奖的事的,(害,咱想都不敢想),直到有一天, CSDN 的小姐姐主动来加我微信,怒斥我仍未填写收货地址[捂脸]。。。

经历了一些曲折,前两天我终于还是收到了 CSDN 的奖品:

截屏2020-11-16 21.49.18

感谢 CSDN 🙏。

不过,这不是今天的主题,只是炫耀一下。今天要讲的故事是:

我这个人不懂什么操作系统,于是我用 Go 语言模拟出了一个…

起因

以前看过一篇文章叫做《我这个人不懂什么CPU,于是我用代码模拟出了一个》。这篇文章介绍了一位大佬一言不合用 Go 语言模拟了一台计算机(项目:djhworld/simple-computer)的故事,帅爆了好吗。

这学期有操作系统课,上这个课程的主要收获就是,课上那些活在 PPT 中的算法,就很迷!我是不太喜欢这种方式的,我奉行 Talk is cheap. Show me the code.

所以,我也用 Go 语言模拟了一个“操作系统”——一个拥有标准输入输出与进程间通信的、基于时间片轮转调度的多道程序运行器:cdfmlr/sham (sham 意为:骗局、虚假事物)。

取名叫做 sham 就是希望大家原谅我的标题党行为,事实上,这个东西远谈不上一个“操作系统”,和 djhworld/simple-computer 等类似的优秀项目比起来,,我做的什么都不是。但我只做了不到 10 分钟的设计,并用不到 2k 行代码实现了它。作为业余蒟蒻实现的玩具项目,你还能对它有什么更高的要求呢?

下面,介绍 Sham 系统。

Sham 系统概述

在这里,简要介绍系统中的一些关键设计思路。

至于具体的实现,事实上,这个项目非常简单,而且我写了不少注释,在 git commit message 中也留下了(在我看来)较为详细的说明,如果您不介意,完全可以去读一读源码:

当然,如果你喜欢这个项目,欢迎 Star、Fork、Watch 三连。如果有任何疑问、意见或建议,也欢迎 Issue 和 PR。

抽象结构

这个模拟的操作系统中,主要包含以下抽象:

  • CPU:一个带有互斥锁的结构,单独在一个协程中运行应用程序的线程。
  • 内存:一个无限大的结构,不需要管理。
  • IO设备:一个独立的设备,单独在一个协程中运行,可以输入或输出。
  • 操作系统:包括操作系统基础结构、调度器、系统调用、中断处理程序组。为例简化模型,操作系统单独运行在一个协程中,它本身不需要在 CPU、内存上运行,而是在内部持有 CPU 和内存。
    • 操作系统基础结构:持有并管理 CPU,内存、IO 设备、进程表和中断向量。
    • 调度器:完成进程调度工作的具体算法。
    • 系统调用:为应用程序提供的“内核态”操作接口。
    • 中断处理程序:处理应用程序或操作系统内部发出的各类中断。
  • 进程、线程:为了简化模型,一个进程只能持有且必须持有一个线程。
    • 进程:包括一个可运行的进程、当前状态(运行、就绪、阻塞)以及需要的各种资源(内存等)。
    • 线程:进程具体可执行的部分,包含要运行的“程序代码”以及运行时的上下文。
    • 上下文:包括程序计数器、进程指针、操作系统接口。
  • 具体应用程序:进程的实例(在 sham_test.go 中实现了一些有意思的应用)。

目前的实现中,调度器使用的是一个带时间片轮转的 FCFS 调度器,但可以十分容易地添加、替换新的调度算法。

系统的工作流程

截屏2020-11-13 19.58.42

线程的运行

关于线程的运行,直接看一部分 Thread 具体代码实现:

// Thread 线程:是一个可以在 CPU 里跑的东西。
type Thread struct {
   
	// runnable 是实际要运行的内容
	runnable Runnable
	// contextual 是 Thread 的环境
	contextual *Contextual
}

// Runnable 程序:应用程序的具体的代码就写在这里面
// 每一次返回就代表“一条指令”(一个原子操作)执行完毕,返回值为状态:
//  - StatusRunning 继续运行(如果时间片未用尽)
//  - StatusReady 会进入就绪队列(即 yield,主动让出 CPU)
//  - StatusBlocked 会进入阻塞状态(一般不用。需要阻塞时一般通过中断请求)
//  - StatusDone 进程运行结束。
type Runnable func(contextual *Contextual) int

// 进程的状态
const (
	StatusBlocked = -1
	StatusReady   = 0
	StatusRunning = 1
	StatusDone    = 2
)

// Contextual 上下文:线程的上下文。
type Contextual struct {
   
    // 指向 Process 的指针,可以取用 Process 的资源
	Process *Process
	// 通过 Contextual.OS.XX 调系统调用
	OS OSInterface
	// 程序计数器
	PC uint
}

具体的应用程序把“代码”写在一个 Runnable 型的函数中,这个函数每执行完一个原子操作就应该返回一次。同时 runnable 也可以在执行完任何一个原子操作时,被外部事件强行打断(比如时间片用尽)。

Thread.Run() 实现了上述控制,runnable 的返回、外部的打断都会被 Thread.Run() 捕获。前面的流程图中,「CPU 运行线程」也就是调用 Thread.Run() 方法:

// Run 包装并运行 Thread 的 runnable。
// 该函数返回的 done、cancel 让 runnable 变得可控:
// - 当 runnable 返回,即 Thread 结束时,done 会接收到 Process/Thread 的状态。
// - 当外部需要强制终止 runnable 的运行(调度),调用 cancel() 即可。
func (t *Thread) Run() (done chan int, cancel context.CancelFunc) {
   
	done = make(chan int)

	_ctx, cancel := context.WithCancel(context.Background())

	go func() {
   
		for {
    // 一条条代码不停跑,直到阻塞|退出|被取消
			select {
   
			case <-_ctx.Done(): // 被取消,取消由 CPU 发起
                // 取消时 CPU 会临时置 Status 为需要转到的状态,
				// 这里获取并把这个值传给操作系统
				// 同时把状态重置为 StatusRunning
                //(状态转化需由操作系统完成,这里只是暂时借用这个值,故要还原)
				s := t.contextual.Process.Status
				t.contextual.Process.Status = StatusRunning
				done <- s
				return
			default:
				ret := t.runnable(t.contextual)
                t.contextual.Commit()  // PC++, clockTick()
				if ret != StatusRunning {
    // 结束|阻塞|就绪,交给调度器处理
					done <- ret
					return
				}
			}
		}
	}()

	return done, cancel
}

这里使用 go 语言标准库的 context 包,控制线程运行的取消(外部打断)。如果没有外部打断,runnable 返回时就会将程序计数器和时钟加一。如果 runnable 一直返回 StatusRunning 就可以一直运行下去。在 runnable 内部,通过 contextual.PC 以及例如 switch 的流程控制语句即可实现一条条代码执行的效果。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值