进程与线程

进程

进程定义

进程是操作系统最核心的概念,进程是对正在运行程序的一个抽象。

进程是具有独立功能的程序关于某个数据集合上的一次运行活动,是系统进程资源分配和调度的独立单位。

严格而言,在某一个瞬间CPU只能运行一个进程,但在较长时间间隔下,操作系统可能运行了多个进程,由此导致并行的错觉,实际上只能认为是并发而不是真正的并行。

进程模型

在进程模型中,计算机所有可运行的软件(包括)操作系统,被组织成若干个顺序进程(sequence process),简称进程。进程是正在执行程序的实例,包括程序计数器、寄存器、变量的当前值。进程和程序之间的区别就和做菜与菜谱之间的关系,进程是动态的,程序是静态的。

进程创建

  • 系统初始化
  • 执行了正在运行的进程所调用的进程创建系统调用
  • 用户请求创建一个新进程
  • 一个批处理作业的初始化

守护进程:停留在后台处理诸如电子邮件、Web页面、新闻、打印之类的进程。

Unix创建新进程:fork, exec。
Windows创建新进程:CreateProcess。

在Unix和WIndows中,进程创建后父子进程各自有不同的地址空间。(实际实现中Linux有写时复制以及不可写内存共享)

进程终止

  • 正常退出(资源)Unix,exit(); Windows,ExitProcess
  • 出错退出(自愿)不正确使用,进程可检测到的错误
  • 严重错误(非自愿)非法指令,引用不存在的内存,除零
  • 被其他进程杀死(非自愿)Unix,kill; Windows,TerminateProcess

进程层次

Unix中,进程和它的所有的子女以及后裔共同组成一个进程组。进程树以init进程为根的一棵树。Windows进程之间的地位是平等的。

进程状态

在多进程系统中,由于某一瞬间只有一个进程独占CPU,而进程在运行时可能需要等待某些条件满足后才能继续运行,此时进程必须暂停以等待条件满足,由此引出进程的三种基本状态:

  • 运行态:此时进程实际占用CPU
  • 就绪态:进程已经准备好可以运行,但是由于CPU正在使用而暂时停止
  • 阻塞态:进程在等待外部条件的满足或者外部事件的发生,否则进程不能运行

如下图为三种基本状态之间的转换关系

进程状态转换

  1. 运行态->阻塞态:比如等待某种I/O操作,像等待用户输入,还有可能是进程调用等待函数主动进入阻塞
  2. 运行态->就绪态:调度程序调度另外一个进程使用CPU,一般为时间片用完、高优先级抢占等。
  3. 就绪态->运行态:调度程序选中当前进程上CPU
  4. 阻塞态->就绪态:外部事件已发生,进程由阻塞态转变为就绪态,如果此时CPU空闲可能立刻调度转变为运行态

进程实现

为了实现进程模型,系统维护一张进程表,每个进程占用一个项,该项被称为进程控制块(Process Control Block, PCB),进程控制块是操作系统用于管理进程而设计的专用数据结构,其主要内容可分为:

  • 进程描述信息

用描述一个进程的基本信息,比如进程标识符(process ID, PID)、进程名、用户标识符、进程组关系等。

  • 进程控制信息

用于操作系统控制进程的运行,包括当前状态、优先级、代码执行入口地址、程序磁盘地址、运行统计时间(执行时间、页面调度)、进程同步与通信、进程队列指针、进程消息队列指针。

  • 拥有资源及使用情况

保存当前进程虚拟地址空间的状况、打开的文件等资源情况。

  • CPU现场信息

保存CPU寄存器值(通用寄存器、程序计数器PC、程序状态字PSW、栈指针等)、进程页表指针。

从操作系统作为资源管理者的角度而言,PCB的主要内容也可分为以下几类:

  • 进程管理

保存进程基本管理信息,诸如寄存器、程序计数器、程序状态字、堆栈指针、进程状态、优先级、进程ID等

  • 存储管理

保存正文段指针、数据段指针、堆栈段指针

  • 文件管理

保存根目录、当前工作目录、文件描述符等

进程控制

进程控制是指控制进程完成不同状态之间的转换操作,使用特定功能的原语(又称为原子操作,是完成特定功能的一段程序,具有不可分割性或不可中断性,在实际实现中可通过屏蔽中断实现)完成。进程控制的原语主要有:

  • 进程创建原语

    1. 给新进程分配一个唯一标识以及进程控制块
    2. 分配地址空间
    3. 初始化控制块
    4. 将进程插入到就绪队列中
  • 进程撤销原语

    1. 收回进程占用的资源,如关闭打开的文件、断开网络链接、回收内存
    2. 撤销该进程的进程控制块PCB
  • 阻塞原语

    1. 有进程自己执行阻塞原语,等待事件发生
    2. Unix, wait; Windows, WaitForSingleObject
  • 唤醒原语

  • 挂起原语

  • 激活原语

  • 优先级改变原语

Unix进程控制操作

  • fork

fork通过复制调用进程来新建一个进程,其基本过程如下

  1. 为子进程分配一个空闲的进程描述符proc(即PCB)结构
  2. 给子进程分配唯一进程标识PID
  3. 以一次一页的方式复制父进程的地址空间(Linux使用COW技术,Copy On Write)
  4. 从父进程出继承共享资源比如打开的文件和当前工作目录等
  5. 将子进程设为就绪态并插入就绪队列
  6. 在父进程中返回子进程的PID值,子进程中返回0,即一次调用两次返回
  • exec

由一系列系统调用组成,通过一段新的程序代码覆盖掉原来的地址空间,实现执行代码的转换

  • wait

提供初级的同步操作,使一个进程等待另外一个一个进程的结束

  • exit

终止一个进程的运行

进程分类

  1. 第一种分类

    • 系统进程
    • 用户进程
  2. 第二种分类

    • 前台进程
    • 后台进程
  3. 第三种分类

    • CPU密集型进程
    • IO密集型进程

进程和程序的区别

  • 进程能够准确刻画并发,程序不能
  • 进程是动态的,而程序是静态的
  • 进程有生命周期,有创建终止,而程序则相对长久的
  • 一个进程能对应一个程序,而一个程序可以对应多个进程。

进程地址空间

每个进程有自己独立的地址空间,对于采用虚拟内存管理的操作系统,地址空间的地址是虚拟地址,需要进行映射以转换到实际物理内存地址。基本结构如下图所示:
进程地址空间

进程映像(IMAGE)

和进程地址空间关联的一个概念为进程映像,其是指对进程执行活动全过程的静态描述,由地址空间内容、硬件寄存器、相关的内核数据结构、内和栈组成。是某一瞬间进程的快照。具体内容有:

  • 用户相关:进程地址空间(代码段、数据段、堆和栈、共享库等)
  • 寄存器相关:程序计数器、指令寄存器、程序状态寄存器、栈指针、通用寄存器等
  • 内核相关
    1. 静态部分:PCB以及各种资源结构
    2. 动态部分:内和栈

进程上下文(CONTEXT)切换

将CPU硬件从一个进程换到另外一个进程的过程称为上下文切换。当进程运行时,其硬件状态保存在CPU的寄存器中;当进程不运行时,这些寄存器保存在进程控制块PCB中。如果切换进程则需要将PCB控制块保存的寄存器信息送入CPU寄存器中。

线程基本概念

进程是一个操作系统资源分配与调度的基本单位,但是在实际应用时,一个应用内部可以划分为多个不同的执行线路或者说细分为更小的任务,执行线路在某种程度上可以进行一定程度的并行,同时执行线路之间通过共享地址空间来协作完成整个任务。而进程作为资源分配与调度的基本单位,如果将这两者分离,让进程完成资源的集中,线程完成实际的执行过程。由此引入线程的概念。至此进程成为资源的分配单位而线程分别称为CPU的实际调度单位。也可以认为线程是进程中的一个运行的实体。

线程的属性

线程主要具有如下属性:

  • 有标识符ID
  • 有状态及状态转换
  • 需要保存上下文环境
  • 有自己的栈和指针
  • 共享进程的地址空间和其他资源
  • 线程可撤销和创建

实际在进程创建后程序以单线程的方式运行,通过thread_create等系统调用可以创建新线程。

线程的实现

在线程的实际实现中,同有如下三种方法:

  • 用户级线程

在用户空间建立线程库,通过运行时系统进行线程的管理,此时内核对于线程一无所知。由于线程也存在状态的转换,因此和进程类似,在每个进程中需要有专用的线程表来跟踪记录各个线程的属性(如程序计数器、堆栈指针、寄存器和状态等),线程表由运行时系统进行管理。

用户级线程由于自身的特点,主要有如下优缺点:

  1. 优点

    1. 线程与线程之间切换速度快,只需要切换堆栈指针程序计数器等,通常几条指令就可完成线程的切换
    2. 线程调度算法可以由每个进程自己定制
    3. 用户级线程可以方便在任何系统中实现,只需实现线程库即可
  2. 缺点

    1. 由于内核对线程一无所知,此时系统调度的单位为进程,因此多线程只能在一个CPU上运行
    2. 由于多数系统调用是阻塞的,一旦线程调用了阻塞的系统调用,内核将阻塞整个进程,即所有的线程被阻塞
  • 核心级线程

核心级线程需要改造操作系统,由内核直接管理所有的线程。此时进程无需专用的线程表而改由内核来记录线程表。此时线程调度有内核完成,当一个线程被阻塞时,内核可以选择运行进程中的另外一个线程而不是直接将整个进程阻塞。在核心级线程实现中,内核需要维护进程表和线程表。

核心级线程实现通常有如下优缺点:

  1. 优点
    1. 内核无需新的非阻塞的系统调用
    2. 线程阻塞不会导致整个进程的阻塞
  2. 缺点
    1. 在内核中创建或者撤销线程的代价比较大
    2. 需要改造操作系统
  • 混合实现

由于用户级线程实现以及核心级线程实现均有其优缺点,通过综合两者的优点从而形成混合实现。在这种是实现中,由编程人员决定有多少个内核级线程与用户级线程,内核只识别内核级线程并进行调度。多个用户级线程通过多路复用来复用多个内核级线程,如下图为多路复用模型:

多路复用模型