进程、线程、协程

进程

进程的出现主要解决了以下两个问题

  1. 可以允许个程序并发执行。
  2. CPU等待IO的问题得到了解决,大幅度提高了CPU的效率。

进程是系统进行资源分配与调度的基本单位。每个进程有独立的资源,包括:

那个程序的可运行机器码的一个在存储器的映像。 分配到的存储器(通常包括虚拟内存的一个区域)。存储器的内容包括可运行代码、特定于进程的数据(输入、输出)、调用堆栈、堆栈(用于保存运行时运数中途产生的数据)。 分配给该进程的资源的操作系统描述符,诸如文件描述符(Unix术语)或文件句柄(Windows)、数据源和数据终端。 安全特性,诸如进程拥有者和进程的权限集(可以容许的操作)。 处理器状态(内文),诸如寄存器内容、物理存储器寻址等。当进程正在运行时,状态通常储存在寄存器,其他情况在存储器。(来自百度百科)

进程的切换

进程的切换表示从正在运行的进程中收回处理器,保存上下文(将CPU寄存器中的数据,虚拟地址空间的切换等等)至该进程私有的堆栈空间中。从而把处理器的寄存器腾出来让其他进程使用。

占用CPU的进程相当于将保存下来的上下文恢复至寄存器中,由上次的断点处继续运行程序。

进程的三态

就绪状态(Ready)

进程已获得除处理器外的所需资源,等待分配处理器资源,只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。

运行状态(Running)

进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。

阻塞状态(Blocked)

由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。

进程的五态

新增了:新建态、终止态

新建态

在开始创建进程,未进入就绪态之前,需要为这个新进程创建必要的管理信息,此时,为新建态。

终止态

当程序运行结束(运行态 -> 终止态),运行时出现无法克服的错误(运行态 -> 终止态),被操作系统所终结(任意 -> 终止态),当前进程被父进程终结(就绪态/阻塞态 -> 终止态)

在进程到达终止态后,进程不再执行,等待完成后续善后操作。

进程调度控制

进程调度方式

非剥夺方式

分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生某事件而阻塞时,才把处理机分配给另一个进程。

剥夺方式

当一个进程正在运行时,系统可以基于某种原则,剥夺已分配给它的处理机,将之分配给其它进程。剥夺原则有:优先权原则、短进程优先原则、时间片原则。

调度算法

批处理系统

先来先服务(First Come First Serve)

算法总是把处理机分配给最先进入就绪队列的进程,一个进程一旦分得处理机,便一直执行下去,直到该进程完成或阻塞时,才释放处理机。
例如,有三个进程P1、P2和P3先后进入就绪队列,它们的执行期分别是21、6和3个单位时间,对于P1、P2、P3的周转时间为21、27、30,平均周转时间为26。

可见,FCFS算法服务质量不佳,容易引起作业用户不满,常作为一种辅助调度算法。

短任务优先

最短CPU运行期优先调度算法(SCBF–Shortest CPU Burst First)
该算法从就绪队列中选出下一个“CPU执行期最短”的进程,为之分配处理机。

只能根据历史进行预测,无法准确了解每个进程 CPU执行时间。

高响应比优先

综合考量进程的两个属性:等待时间和要求服务时间——等待时间长,要求服务时间短(就是短进程)的进程更容易被选中。
响应比 = (等待时间+要求服务时间)/ 要求服务时间。响应比高的算法会先执行。

分时系统

时间片轮转算法(Round Robin,RR)

每个进程按照一定的时间间隔轮流使用使用 CPU 资源,当时间到达规定时间或者进程被阻塞,进行进程切换,切换进程的选择类似 FCFS。

虚拟轮转法 (Virtual Round Robin,VRR)

由于RR算法,会过早的将阻塞的进程切换,所以IO密集型进程真正得到的CPU时间比较少,所以增加一个辅助队列。阻塞解除的进程,将进入这个辅助队列,进行进程调度时,优先选择辅助队列里的进程。

优先级调度

赋予每个进程一个优先级,系统进程优先级高于用户进程优先级。从高优先级进程向低优先级进程选择,为了保证低优先级进程不会饥饿,长时间未调度的进程会提高优先级。

线程

线程是操作系统进行运算调度的最小单位。线程属于进程,一个进程中可以并发多个线程,每条线程并行执行不同的任务。同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。

线程的同步

由于同属一个进程下的多个线程拥有当前进程的全部资源,同时对资源进行操作,会出现问题,所以需要使用,锁,条件变量,信号量等方式进行同步。

线程切换与进程切换区别

进程切换与线程切换的一个最主要区别就在于进程切换涉及到虚拟地址空间的切换而线程切换则不会。因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。

每个进程都有属于自己的页表,保存了当前进程虚拟地址与物理地址的映射关系。页表查找的速度相对比较慢,CPU会将页表查找的结果缓存至它的TLB(translation Lookaside Buffer)中。当进程切换后,全部或部分的缓存会失效,导致命中率降低。虚拟地址映射到物理地址的速度会变慢,表现出来的就是程序运行会变慢。

而同一个进程下的线程的切换不涉及虚拟地址的切换,所以线程切换比进程切换开销更低,速度更快。

协程

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。

  • 传统意义上的协程一般只存在与单线程中,不存在并行执行。
  • 协程的运行机制属于协作式任务处理,需要控制主动交出控制权。

goroutine

golang 的协程是一种特殊的协程,通过语言本身实现的GMP调度器,对内核线程以及goroutine进行调度。
有以下几点与正常的coroutine不同

  1. goroutine 可以被 GMP 调度至多个线程上完成运行。
  2. goroutine 属于抢占式任务处理,GMP模型会将长时间运行的 goroutine暂停,让出资源。
  3. 由于存在并行可能性,所以需要通过 channel 或 sync 进行同步。

协程对比线程的特点

  1. 占用空间更加小,一般都是KB级别。
  2. 线程的切换需要陷入内核进行上线文切换,而协程的切换是在用户主动完成。
  3. 增加开发效率,同步的编码方式实现一些耗时的IO操作异步化。
-------- 本文结束 感谢阅读 --------

本文标题:进程、线程、协程

文章作者:zyh

发布时间:2021年09月19日 - 11:25

最后更新:2021年09月19日 - 23:21

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持技术分享,您的支持将鼓励我继续创作!
0%