博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
学会 C 语言真可以开发很多东西吗?
阅读量:4120 次
发布时间:2019-05-25

本文共 2021 字,大约阅读时间需要 6 分钟。

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 获取代码!

转载地址:http://skspi.baihongyu.com/

你可能感兴趣的文章
SoapUI-入门
查看>>
Oracle -常用命令
查看>>
JAVA技术简称
查看>>
ORACLE模糊查询优化浅谈
查看>>
2016——个人年度总结
查看>>
2017——新的开始,加油!
查看>>
【Python】学习笔记——-6.2、使用第三方模块
查看>>
【Python】学习笔记——-7.0、面向对象编程
查看>>
【Python】学习笔记——-7.2、访问限制
查看>>
【Python】学习笔记——-7.3、继承和多态
查看>>
【Python】学习笔记——-7.5、实例属性和类属性
查看>>
Linux设备模型(总线、设备、驱动程序和类)之四:class_register
查看>>
git中文安装教程
查看>>
虚拟机 CentOS7/RedHat7/OracleLinux7 配置静态IP地址 Ping 物理机和互联网
查看>>
弱类型、强类型、动态类型、静态类型语言的区别是什么?
查看>>
Struts2技术内幕图书 转载
查看>>
Java异常分类
查看>>
项目中的jackson与json-lib使用比较
查看>>
Jackson Tree Model Example
查看>>
j2ee-验证码
查看>>