什么是进程

定义

进程就是应用程序的启动实例。比如我们运行一个游戏,打开一个软件,就是开启了一个进程。

进程拥有代码和打开的文件资源、数据资源、独立的内存空间。 进程是资源分配的最小单位。 理解资源分配 在单核CPU中,同一时刻只有一个程序在内存中被CPU调用运行

假设有A、B两个程序,A正在运行,此时需要读取大量输入数据(IO操作),那么CPU只能干等,直到A数据读取完毕,再继续往下执行,A执行完,再去执行程序B,白白浪费CPU资源。

进程的结构

1.程序:描述进程要完成的功能及如何完成;

2.数据集:程序在执行过程中所需的资源; 3.进程控制块:记录进程的外部特征,描述执行变化过程,系统利用它来控制、管理进程,系统感知进程存在的唯一标志。

进程三态模型

  • 运行态(Runing):时刻进程占用 C P U
  • 就绪态(Ready):可运行,但因为其他进程正在运行而暂停停止
  • 阻塞状态(Blocked):该进程等待某个事件(比如IO读取)停止运行,这时,即使给它CPU控制权,它也无法运行

image.png

  1. C P U 调度就绪态进程执行,进入运行状态,时间片使用完了,回到就绪态,等待 C P U 调度
  2. C P U 调度就绪态进程执行,进入运行状态,执行IO请求,进入阻塞态,IO请求完成,CPU收到 中断 信号,进入就绪态,等待 C P U 调度

进程五态模型

在三态基础上,做一次细化,出现了另外两个基本状态,创建态和结束态。 image.png

  • 创建态(new):进程正在被创建
  • 就绪态(Ready):可运行,但因为其他进程正在运行而暂停停止
  • 运行态(Runing):时刻进程占用 C P U
  • 结束态(Exit):进程正在从系统中消失时的状态
  • 阻塞状态(Blocked):该进程等待某个事件(比如IO读取)停止运行,这时,即使给它CPU控制权,它也无法运行

状态的变迁

  • NULL => 创建态(new):一个新进程被创建时的第一个状态
  • 创建态(new) => 就绪态(Ready):当进程创建完成,进入就绪态
  • 就绪态(Ready)=> 运行态(Runing):C P U 从就绪队列选择进程执行,进入运行态
  • 运行态(Runing)=> 结束态(Exit):当进程已经运行完成或出错时,进入结束状
  • 运行态(Runing) => 就绪态(Ready):分配给进程的时间片使用完,进入就绪态
  • 运行态(Runing) => 阻塞状态(Blocked): 进程执行等待事件,进入阻塞态
  • 阻塞状态(Blocked) => 就绪态(Ready):进程事件完成,C P U 收到 中断 信号 ,进入就绪态

进程七态模型

其实进程还有一种状态叫挂起态,挂起态代表该进程不会占用内存空间,它会被换出到硬盘空间保存,当需要使用它的时候,会被换入,加载到内存,挂起态可以分为下面两种

  • 阻塞挂起状态:进程在外存(硬盘)并等待某个事件的出现
  • 就绪挂起状态:进程在外存(硬盘),但只要进入内存,即刻立刻运行

image.png 从上图我们发现,创建态、就绪态、运行态,阻塞挂起态、阻塞态都可以转入挂起态,这时问题就产生了,什么情况会转入 挂起态 ,什么情况又会从 挂起态 转入到 非挂起态(就绪态与阻塞态), 操作系统会根据当前资源状况和性能要求、进程的优先级来进行挂起与激活操作,没有固定的说法。

进程的上下文切换

C P U把一个进程切换到另一个进程运行的过程,称为进程上下文切换。

进程上下文切换流程

首先进程是由内核管理与调度的,所以 进程上下文切换 发生在内核态,进程上下文切换的内容包含用户空间资源(虚拟内存、栈、全局变量等)与内核空间资源(内核堆栈、寄存器等)。 在做上下文切换的时候,会把前一个 进程 的上下文保存到它的 P C B 中,然后加载当前 进程 的 P C B 上下文到 C P U 中,使得 进程 继续执行

进程上下文切换场景

  • 为了保证所有进程可以得到公平调度,CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。这样,当某个进程的时间片耗尽了,切换到其它正在等待 CPU 的进程运行
  • 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并由系统调度其他进程运行。
  • 当进程通过睡眠函数 sleep 这样的方法将自己主动挂起时,自然也会重新调度。
  • 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行
  • 发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序。

什么是线程

线程从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。 线程拥有自己的栈空间。

线程的状态

什么是协程

协程的两种实现方式

这里我们想说的一点是所谓的有栈,无栈并不是说这个协程运行的时候有没有栈,而是说协程之间是否存在调用栈(callbackStack)

有栈协程

无栈协程

进程和线程的区别

进程和线程的主要区别是,操作系统的资源管理方式不同,进程有独立的地 址空间,当一个进程崩溃后,不会影响其他进程。而线程是一个进程中不同的执行路径,它 有自己的堆栈和局部变量,但是没有单独的地址空间。

  1. 关系
    1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
  2. 资源占用
    1. 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。)
  3. 调度控制
    1. 进程是资源分配的最小单位,线程是CPU调度的最小单位
  4. 系统开销
    1. 由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/o设备等。因此,操作系统所付出的开销将显著地大于在创建或撤消线程时的开销。类似地,在进行进程切换时,涉及到整个当前进程CPU环境的保存以及新被调度运行的进程的CPU环境的设置。而线程切换只须保存和设置少量寄存器的内容,并不涉及存储器管理方面的操作。可见,进程切换的开销也远大于线程切换的开销
  5. 通信
    1. 由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现,也变得比较容易。进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性

协程和线程的区别

  1. 调度方式
    1. 协程由编程者控制,协程之间可以有优先级;线程由系统控制,一般没有优先级
  2. 调度速度
    1. 协程几乎比线程快一个数量级。协程调用由编码者控制,可以减少无效的调度
  3. 资源占用
    1. 协程可以控制内存占用量,灵活性更好;线程由系统控制
  4. 创建数量
    1. 协程的使用更灵活(有优先级控制、资源使用可控),调度速度更快,相比于线程而言调度损耗更小,所以真实可创建且有效的协程数量可以比线程多很多,这是使用协程实现异步编程的重要基础。同样因为调度与资源的限制,有效协程的数量也是有上限的

调度策略

抢占 vs 协作

从调度机制上来讲,调度可以分为抢占式和协作式。