学会 C 语言真可以开发很多东西吗?

640?wx_fmt=jpeg


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

Chat 作者 | Allen()

整理 | linse


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


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


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


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

  • 控制流切换原理

  • 上下文切换

  • 线程设计

  • 调度函数的封装与代码模块化

  • 线程的主动切换

  • 时间片轮转调度


本文实验环境:

  • ubuntu 16.04 32 位操作系统(读者请务必提前安装好);

  • 挑选一个你自己觉得好用的虚拟机软件,比如 VMWare;

  • 请把你的虚拟机环境配置成单核 CPU。


学习时间:大于 5 小时


本场 Chat 作者——Allen()

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


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

扫码查看完整 Chat 原文

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

640640

640?wx_fmt=jpeg

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


需要注意的是,本文的代码,并没有使用操作系统为我们提供的 pthread 系列函数thread_create  thread_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 点:

  • 封装线程创建函数

  • 完成一个简单的调度算法

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


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

640?wx_fmt=gif

图1 用户态线程运行示例


本场 Chat 关键点总结:

  • 理解“栈切换”的本质

  • 掌握指令执行与栈的关系

  • 掌握上下文是如何切换的

  • 理解主动切换,知道主动切换可能会发生在哪些地方

  • 理解什么是时间片

  • 对于不主动让出 cpu 的线程,是如何切换到其它进程的

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值