现代操作系统:进程与线程

进程是操作系统的核心。
进程可以看做是一个程序的实例,如果一个程序运行了两遍,则存在两个进程。

1. 进程

进程的层次结构

在UNIX中,进程和它的所有子女以及后裔共同组成一个进程组。
但是在Windows中,没有进程层次的概念,所有的进程都是地位相同的。

进程的状态

进程有三种状态:

  • 运行态(该时刻进程实际占用CPU)
  • 就绪态(可运行,但因为其他进程正在运行而暂时停止)
  • 阻塞态(除非某种外部事件发生,否则进程不能运行)

前两种状态在逻辑上是类似的,第三种状态与前两种状态不同,处于该状态的进程不能运行,即使CPU空闲也不行。
进程的这三种状态之间有4种可能的转换关系:

  • 运行 -> 就绪
  • 就绪 -> 运行
  • 运行 -> 阻塞
  • 阻塞 -> 就绪

考虑用户进程、磁盘进程、终端进程 ,这些进程在等待时总是处于阻塞状态,在已经读入磁盘或键入字符后,等待它们的进程就被解除阻塞,并成为可调度运行的进程。

进程的实现

操作系统维护着一张表格(一个结构数组),即进程表。每个进程占用一个进程表项,该表项包含了进程状态的重要信息,包括程序计数器、寄存器、堆栈指针等。

多道程序设计

采用多到程序设计可以提高CPU的利用率。
假设计算机有512MB内存,操作系统占用128MB,每个用户程序占用128MB,这些内存空间允许3个用户程序同时驻留在内存中。
若80%的时间用于I/O等待,则CPU的利用率(忽略操作系统开销)大约是1-0.8^3,即大约49%。
在增加512MB内存后,可从3道程序设计提高到7道程序设计,因而CPU利用率提高到79%。
换言之,第二个512MB内存提高了30%的吞吐量。


通过多道程序设计中多个进程之间来回切换,系统制造了不同的顺序进程并行运行的假象。

2. 线程

在传统操作系统中,每个进程有一个地址空间和一个控制线程。
线程可以理解为是共享同一地址空间和所有可用数据的迷你进程。

经典的线程模型

在同一个进程中并行运行多个线程,是对在同一台计算机上并行运行多个进程的模拟。
前者是多个线程共享同一个地址空间和其他资源。
后者是多个进程共享物理内存、磁盘、打印机和其他资源。


和多道程序设计类似,多线程中CPU在线程之间的快速切换,制造了线程并行运行的假象,好似它们在多个比实际CPU慢一些的CPU上同时运行。


进程中的不同线程不像不同进程之间那样存在很大的独立性,所有线程都有完全一样的地址空间,共享同样的全局变量。
但每个线程有其自己的堆栈。

在用户空间中实现线程

有两种主要的方法实现线程包:在用户空间中和在内核中。
如果把整个线程包放在用户空间中,内核对线程包一无所知。从内核角度考虑,就是单线程进程。
在用户空间管理线程时,每个进程需要有其专用的线程表,用来跟踪该进程中的线程。

在内核中实现线程

在这种情况下,每个进程没有线程表,相反,在内核中有用来记录系统中所有线程的线程表。当一个线程阻塞时,内核根据其选择,可以运行同一个进程中的另一个线程(若有一个就绪线程),或者运行另一个进程中的线程。

调度程序激活机制

调度程序激活的的目标是模拟内核线程的功能,但是为线程包提供通常在用户空间中才能实现的更好的性能和更大的灵活性。

弹出式线程

当一个消息的到达导致系统创建一个处理该消息的线程,这种线程称为弹出式线程。该方式的好处是:由于没有历史,每个线程的创建十分迅速。

3. 进程间通信

进程间通信要解决3个问题:

  • 一个进程如何把消息传递给另一个;
  • 确保两个或多个进程在关键活动中不会交叉,如订票系统争夺最后一个座位;
  • 确保进程间顺序正确。

竞争条件

当两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序,称为竞争条件
要避免这种错误,关键是要找出某种途径来阻止多个进程同时读写共享的数据,即互斥
把对共享内存进行访问的程序片段称作临界区域,如果我们能够使得进程不可能同时处于临界区中,就能够避免竞争条件。

几种实现互斥的方案

  • 屏蔽中断
  • 锁变量
  • 严格轮换法(忙等待)
  • Peterson解法
  • TSL指令:硬件方式锁住内存总线,禁止其他CPU在本指令结束之前访问内存

Peterson解法和TSL指令都是正确的,本质上:当一个进程想进入临界区时,先检查是否允许进入,若不允许,则该进程原地等待,直到允许为止。

信号量

使用一个整形变量来累计唤醒次数,一个信号量的取值可以是0(表示没有保存下来的唤醒操作),也可以是正值(表示有1个或多个唤醒操作)。

互斥量

互斥量是信号量的简化版本,它是一个可以处于两种状态的变量:解锁和加锁。
当一个线程(或进程)需要访问临界区时,如果该互斥量是解锁的,调用线程可以自由进入该临界区。
如果该互斥量已经加锁,调用线程被阻塞。如果多个线程被阻塞在该互斥量上,将随机选择一个线程并允许它获得锁。

4. 调度

当多个进程或线程同时竞争CPU时,必须选择下一个要运行的进程,完成选择工作的这一部分操作系统称为调度程序。
某些进程花费绝大多数时间在计算上,称为计算密集型;有的进程则在等待I/O上花费了绝大多数时间,称为I/O密集型

何时调度

存在着需要调度处理的各种情形。

  1. 决定是运行父进程还是运行子进程。
  2. 当一个进程退出时,从就绪进程中选择另外某个进程。
  3. 当一个进程被阻塞时,必须选择另一个进程运行。
  4. 在一个I/O中断发生时,必须做出调度决策。

硬件时钟提供50Hz、60Hz或其他频率的周期性中断。
当时钟中断发生时,如果不进行调度,而是让进程运行直至被阻塞,或者直到进程自动释放CPU,这种调度方式叫做非抢占式调度算法
如果让当前进程运行某个固定时段,如果时段结束时进程仍在运行,它就被挂起。这种调度方式叫做抢占式调度算法

调度算法分类

不同的应用领域有不同的目标,大致有三种环境:

  • 批处理:非抢占式调度,商业领域广泛应用;
  • 交互式:抢占式调度,如服务器;
  • 实时

当然无论是何种情况,有些调度优化的目标是相同的:

  • 公平:给每个进程公平的CPU份额
  • 策略强制执行:看到所宣布的策略执行
  • 平衡:保持系统的所有部分都忙碌

运行大量批处理作业的大型计算中心通常检查3个指标:

  1. 吞吐量:系统每小时完成的作业数量;
  2. 周转时间:从一个批处理作业提交时刻开始直到该作业完成时刻为止的统计平均时间;
  3. CPU利用率

对于交互式系统,最重要的是最小响应时间,即从发出命令到得到响应之间的时间。能够让所有交互式请求首先运行的则是好服务。

对于实时系统,最主要的要求是满足所有(或大多数)截止时间的要求。

批处理系统中的调度

1. 先来先服务

在所有调度算法中,最简单的是非抢占式的先来先服务算法。进程按照它们请求CPU的顺序使用CPU,是基于一个就绪进程的单一队列。
要选取一个进程运行,只要从该队列的头部移走一个进程即可;要添加一个新的作业或阻塞一个进程,只要把该作业或进程附加在队列的末尾即可。

2. 最短作业优先

顾名思义,最短作业时间调度就是将花费时间最短的作业排在前面。这也是非抢占式的批处理调度算法。

3. 最短剩余时间优先

最短作业优先的抢占式版本是最短剩余时间优先算法。调度程序总是选择剩余运行时间最短的那个进程运行。
当然这种算法需要提前掌握作业的运行时间。
当一个新的作业到达时,其整个时间同当前进程的剩余时间作比较,如果新的进程比当前运行进程需要更少的时间,当前进程就被挂起,而运行新的进程。

交互式系统中的调度

1. 轮转调度

每个进程被分配一个时间段,称为时间片。如果在时间片结束时该进程还在运行,则将剥夺CPU并分配给另一个进程;如果该进程在时间片结束前阻塞或结束,则CPU立即进行切换。
进程切换,有时称为上下文切换,需要切换内存映像、清除和重新调入高速缓存。
时间片设得太短,会导致过多的进程切换,降低了CPU的效率;而设得太长又可能引起对短的交互请求的响应时间变长。
将时间片设为20ms~50ms通常是一个比较合理的折中。

2. 优先级调度

优先级调度的基本思想很清楚:每个进程被赋予一个优先级,允许优先级最高的可运行进程先运行。
优先级可以是静态赋予或动态赋予的。

3. 彩票调度

向进程提供各种系统资源(如CPU时间)的彩票,一旦需要做出一项调度决策时,就随机抽出一张彩票,拥有该彩票的进程获得该资源。
在应用到CPU调度时,系统可以掌握每秒钟50次的一种彩票,作为奖励每个获奖者可以得到20ms的CPU时间。

实时系统中的调度

实时系统是一种时间起着主导作用的系统,如医院特别护理部门的病人监护装置、飞机自动驾驶系统。
实时系统可以分为硬实时软实时。前者的含义是必须满足绝对的截止时间,后者的含义是虽然不希望偶尔错失截止时间,但是可以容忍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值