前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用 C 语言实现操作系统

用 C 语言实现操作系统

作者头像
CSDN技术头条
发布2019-05-15 11:32:46
2.1K0
发布2019-05-15 11:32:46
举报
文章被收录于专栏:CSDN技术头条CSDN技术头条

本文选自 | 《操作系统基础:C 语言实现用户线程》

Chat 作者 | Allen()

整理 | linse

码农都懂的一句话:C 语言无所不能,什么都能搞。把 C 学好,上手其他语言也会事半功倍。因为热爱 C 语言,所以才有了以下这篇文——

一直以来,我们学习线程切换与调度,都是通过阅读操作系统书籍或 Linux 源码甚至反汇编 Window 内核代码。无论怎样,这些东西都很抽象,学习起来十分困难。

另外,随着现代化编程语言逐渐流行,C++20 的正式发布,都离不开用户态线程这些基础知识。再比如 Golang 的 Goroutine,腾讯公司的开源的 libco,百度的 BRPC 中的 bthread,如果想深刻理解它们,都需要扎实的基本功

本文会带你循序渐近的掌握以下知识点:

  • 控制流切换原理
  • 上下文切换
  • 线程设计
  • 调度函数的封装与代码模块化
  • 线程的主动切换
  • 时间片轮转调度

本文实验环境:

  • ubuntu 16.04 32 位操作系统(读者请务必提前安装好);
  • 挑选一个你自己觉得好用的虚拟机软件,比如 VMWare;
  • 请把你的虚拟机环境配置成单核 CPU。

学习时间:大于 5 小时

本场 Chat 作者——Allen()

百度高级研发工程师,CSDN 博客专家。目前从事智能交互式对话系统云端开发。负责过大流量、高并发等业务场景。

时间紧张,心急看全文的朋友来这里

扫码查看完整 Chat 原文

还能进入读者圈与作者深度交流

回归正文,我为什么要使用 32 位系统?因为方便初学者学习,也能更快速的掌握原理。

需要注意的是,本文的代码,并没有使用操作系统为我们提供的 pthread 系列函数,thread_createthread_join 函数都是自己纯手工实现的。唯一使用操作系统的函数就是设置时钟,因此会有时钟信号产生,这一步是为了模拟时间片轮转算法而做的。

01

控制流切换原理

控制流,指的是一系列按顺序执行的指令。比如你编写的多线程程序,每个线程就可以看成是一个控制流,多个线程允许多个控制流一起执行。

在我们学习编程的时候,如果不借助操作系统提供的线程框架,几乎无法完成多控制流的运行的。

接下来剖析一下,我们的指令如何”莫名奇妙“的就切换到其它线程的。

1.1 指令执行

不管你用的是什么语言编程,最后都要落实到 CPU 上,而 CPU 只认识它自己的语言,机器语言。机器语言可以抽象出对应 CPU 架构的汇编指令。

对于单核 CPU 来说,永远只有一条控制流,也就是只有一条指令序列。所以,宏观上模拟的多线程程序,本质上还只是单控制流,所谓的多线程,只不过是一种被制造出来的假像!

1.2 控制流切换(x86 CPU 架构)

汇编指令在执行的时候,最重要地方在于它需要依赖 CPU 环境:

  • 一套通用寄存器 (eax、edx、ecx、ebx、esp、ebp、esi、edi);
  • 标志寄存器 eflags;
  • 指令寄存器 eip (eip 用来保存下一条要被指令的地址)。

如果你不理解 CPU 寄存器是什么意思,把它想象成它是 CPU 中预先定义好的变量。

1.3 再论控制流切换

我们换个思路,不修改栈的内容,而是直接换一个栈,本质上也就是换 esp 的值,能不能达到相同的效果?

话说回来,这个真的是控制流“切换”吗,只是看起来像而已,本质上它只是个跳转。

02

上下文切换

切换出去,是完成线程调度,最关键的一点在于还得切换回来。

2.1 上下文切换原理

上下文切换不同于第 1 节所述的暴力切换,因为在那个实验里,我们永远无法重新返回到 main 函数中。如果你想从那个 fun 函数再跳回目的地,我们需要在切换控制流前保存当前寄存器环境,以及当前的栈顶位置。

2.2 保存寄存器环境

有很多种手段保存寄存器环境,最简单的一种就是保存到定义好结构体去。

03

用户态线程设计

相信上下文切换的实验你已经写出来了,不过你肯定还很多不满意的地方:

  • 线程的切换太暴力
  • 代码看起来一箩筐没有组织
  • 看起来根本没有线程的样子

在我们需要对程序进行改进时,主要有以下 2 点:

  • 封装线程创建函数
  • 完成一个简单的调度算法

在完整原文里,我带领大家完成了线程切换与线程调度的框架,在此基础上,还可以探索更多有关线程的东西,比如线程的同步与互斥,还有更多的线程状态。理解了本文之后,更深入的学习对你来说已经不是难事了!

最终的结果和下图的效果一样

图1 用户态线程运行示例

本场 Chat 关键点总结:

  • 理解“栈切换”的本质
  • 掌握指令执行与栈的关系
  • 掌握上下文是如何切换的
  • 理解主动切换,知道主动切换可能会发生在哪些地方
  • 理解什么是时间片
  • 对于不主动让出 cpu 的线程,是如何切换到其它进程的

本文的代码托管在 Github,在文件夹 v5 里,有需要的朋友可以点击阅读原文,订阅这场 Chat 获取代码!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GitChat精品课 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 控制流切换原理
    • 1.1 指令执行
      • 1.2 控制流切换(x86 CPU 架构)
        • 1.3 再论控制流切换
        • 02
        • 上下文切换
          • 2.1 上下文切换原理
            • 2.2 保存寄存器环境
            • 03
            • 用户态线程设计
            • 本场 Chat 关键点总结:
            相关产品与服务
            代码托管
            CODING 代码托管(CODING Code Repositories,CODING-CR)是为开发者打造的云端便捷代码管理工具,旨在为更多的开发者带去便捷、高效的开发体验,全面支持 Git/SVN 代码托管,包括代码评审、分支管理、超大仓库等功能。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档