程序、进程和线程的概念

1. 程序和进程的区别 首先来看一个故事:

A平时因为工作忙,都是在公司食堂吃或者吃外卖。但是突然有一天下班突然心血来潮想自己做饭,于是A来到超市购物,他买了一些食材准备做一份西红柿炒鸡蛋。回到家后,A拿了出放在书架最深处的一本《家常菜食谱大全》,照着上面的方法,他就开始准备做菜了:首先先把西红柿洗干净切块,鸡蛋搅拌成蛋液,开锅热油........终于在A的不屑努力之下,一份色香味俱全的西红柿炒鸡蛋就完成了。
在上述故事中,我们可以抽象出一个简单的计算处理模型:
  • 【程序】就是那本食谱(存储在书架上,一种物理存在,用特定的描述形式阐述做菜的过程和方法)
    注:
    著名计算机科学家Niklaus Wirth提出了:程序 = 数据结构 + 算法
    将食谱看成程序,它就是用图文并茂地形式(数据结构)生动地向读者展示各个菜的做法(算法)
  • 【进程】就是做菜的这一系列活动(动态进行的),包括了洗菜、切菜、炒菜等等
  • 【CPU】就是A本人,他是所有操作的执行者
  • 【数据】就是西红柿炒鸡蛋的各种原材料,包括了西红柿、鸡蛋...(将一系列数据经由CPU处理变成不同的数据)
2. 进程 2.1 进程的状态 进程就是程序关于某个数据集上的一系列活动。
进程有三个状态,分别是:
  • 运行态【running】(actually using the CPU at that instant)
  • 就绪态【ready】(runnable; temporarily stopped to let another process run)
  • 阻塞态【blocked】(unable to run until some external event happens)
程序、进程和线程的概念
文章图片

从上图中,我们可以很清楚地看到三种状态之间的转化过程:
  1. 当发生IO操作时,当前进程就会被阻塞直到IO操作完成
  2. 当调度器中断当前进程转而执行另一进程,原进程就会处于就绪状态
  3. 当调度器选择该进程,便会从就绪态转为运行态,享用CPU资源
  4. 当IO完成后,被阻塞的进程就会转为就绪态,等待操作系统的调度
2.2 进程上下文切换 当进程与进程间因中断(interrupt)或系统调用(system call)时会发生上下文切换
程序、进程和线程的概念
文章图片

  • 正在执行的进程P0由于终端或者系统调用将cpu的使用权交还给os
  • os保存P0的PCB信息,并重新从内存中载入P1进程的pcb内容,并执行P1
  • P1执行后,由中断或者系统调用由交还使用权给os(调度器scheduler / 调度者scheduler)
  • 再回过头来执行P0(载入P0的PCB信息)
上下文切换的代价其实是很高的,需要大量的处理器时间(因此通常使用汇编完成),有时候1秒内可能执行上千次的上下文切换【CONTEXT SWITCH】
即便现代计算机正在逐渐降低上下文切换所占用的CPU时间,但那也仅是在CPU时钟周期降低,处理速度加快的情况下,而不是提升了上下文切换的效率
3. 为什么要引入线程的概念? 既然我们已经有了进程,那么为什么计算机科学家还要搞出一个线程的概念呢?
  1. 在系统中多应用将同时进行,经常会不断切换状态,通过将一个进程切分成多个线程,达到准并行【quasi-parallel】,程序模型将更简单(这些线程共享地址空间和数据资源,这些能力正是多进程无法做到的(地址空间不同))
  2. 更加轻量级。比起进程,更加容易(快速)创建和销毁(在一些系统中,比进程创建快10-100倍)
  3. 性能表现更好。当有大量计算和I/O处理时,线程有能力让这些活动交叠,提升程序的速度
  4. 对多核处理器更有用。
【注】
由于进程是资源的拥有者,所以在创建、撤销、切换操作中需要较大的时空开销,限制了并发程度的进一步提高。
为减少进程切换的开销,把进程作为资源分配单位和调度单位这两个属性分开处理,即进程还是作为资源分配的基本单位,但是不作为调度的基本单位(很少调度或切换),把调度执行与切换的责任交给“线程”。
这样做的好处不但可以提高系统的并发度,还能适应新的对称多处理机(SMP)环境的运行,充分发挥其性能
3.1 线程的一些缺陷 线程虽然运行速度很快,但并不是十分完美的
主要是在安全性方面的问题:我们知道,进程内的各个线程之间是共享数据和地址空间的,如果其中一个线程出现了错误(比如错误改写了某个变量),其他线程也会跟着出现问题,导致最终整个进程的错误
因此,我们说现在对于进程和线程的使用也是分场合的,不是一味的追求线程:
  • 在高性能计算领域(如天气预报、水利、空气动力学等等),这一类需要追求高性能计算,同时程序并不容易出错,就适合使用线程
  • 而例如浏览器这种应用,由于浏览器页面的操作由用户执行,有时候会考量到数据安全,因此推荐使用进程,一个进程打开一个网页
3.2 线程的实现 3.2.1 用户空间实现
程序、进程和线程的概念
文章图片

在用户空间建立线程库,提供一组管理线程的过程由
运行时系统【run-time system】来完成线程的管理工作
内核管理的还是进程,内核不知道线程的存在
线程切换不需要陷入内核
如Unix、Linux
优点 缺点
切换速度快 一进程只有一个线程运行在处理器上
调度算法可以有应用程序设定 若一个进程的某个线程调用了阻塞的系统调用,那么该进程的所有线程也将会被阻塞,页面失效也会有同样的问题
用户级线程可以运行在任何操作系统,包括不支持线程操作系统
3.2.2 内核空间实现
程序、进程和线程的概念
文章图片

内核管理所有的线程管理,创建,撤销与调度**,并向应用程序提供API
内核维护进程和线程上下文
线程的切换需要内核支持
以线程为基础进行调度
优点 缺点
由内核调度,当有多个处理器时,一个进程的多个线程可以在多个处理器上同时执行 由内核进行创建,撤销,调度,系统开销更大
一个进程的某个线程阻塞不会引起其他线程的阻塞,页面失效同理
3.3.3 混合实现
程序、进程和线程的概念
文章图片

线程创建在——用户空间
线程调度在——内核空间
4. 进程和线程之间拥有资源的关系? 【进程】是资源分配的单位——————【线程】是CPU调度的单位
线程 又称为“轻量级进程”【lightweight processes】
它(同一进程的所有线程)共享进程的地址空间,同时还有自己特有的一些信息:
程序、进程和线程的概念
文章图片

每个线程共享进程的信息如上图左侧所示
但,每个线程也有自己专属的 PC、寄存器、栈、状态信息
(因为每个线程可能去调用不同的步骤,
【程序、进程和线程的概念】因此线程之间都有各自的栈、PC、寄存器,这样防止执行时发生异常)
【注】
  • 进程内的多个线程是共存的
  • 相互将信息共享,甚至一个线程创建删除另一个线程都是可能的
  • 虽然进程之间有上下级关系,但线程间没有(所有线程是平等的)
5. 参考资料 《现代操作系统》
《Operating Systems》
操作系统_清华大学(向勇、陈渝)

    推荐阅读