##第二章、进程与线程
操作系统最核心的概念就是进程,它是对正在运行的程序的一个抽象,也可以理解为对处理器的抽象。即使可用的CPU可用,但是依然可以支持多进程(伪)并发操作。
2.1 进程
伪并行:严格的说,在一瞬间,CPU只能运行一个进程,但是在多道程序设计系统中,每个进程各运行几十或几百毫秒,那一秒内CPU就可以同时运行多个进程,给人产生了并行的错觉。
2.1.1 进程模型
一个进程就是一个正在执行的程序,它包括程序计数器(PC)、寄存器、变量的当前值等。为了更好理解,本章假设计算机只有一个CPU。下图是对多个进程在CPU上运行的理解:
在任意时刻,总是只有一个进程在运行。
2.1.2 进程的创建
四种主要事件会导致进程创建:
- 1.系统初始化
- 2.正在运行的程序执行了创建进程的系统调用
- 3.用户请求创建一个进程
- 4.一个屁处理作业的初始化
####2.1.3 进程的终止
四种主要事件会导致进程的终止:
- 1.正常退出(自愿的)
- 2.出错退出(自愿的)
- 3.严重错误(非自愿)
- 4.被其他进程杀死(非自愿)
2.1.4 进程的层次结构
UNIX系统中,进程 创建另一个进程后,父进程和子进程就以某种形式继续保持联系,进程只有一个父进程,但可以有多个子进程。
Windows中没有进程层次的概念,所有进程都是地位相等的,但父进程创建爱你子进程后会得到一个令牌(称为句柄),该句柄可以用来控制子进程,但父进程有权把这个令牌传递给其他进程,这样,就不存在层次概念了。
2.1.5 进程的状态(三种状态)
进程有三种状态: 1.就绪; 2.运行;3.阻塞。进程总是在这三种状态见切换:
进程准备好后就可以运行了,但是这个时候其他程序在运行,因此进入就绪状态,当调度选择了这个进程时,它就可以运行了。而正在运行的进程,由于CPU给的时间用完了,但还没执行完毕,这时就会进入就绪转态,等待下次调度进入CPU。如果正在运行的进程由于需要I/O操作或者其他因素,需要等待一段时间才可以继续运行,就会进入阻塞状态,如果I/O执行完毕,就会进入就绪状态,再次等待运行。
2.1.6 进程的实现
为了实现进程模型,操作系统维护一张表格,即进程表,每个进程占用一个进程表项 (有的作者称这个表项为进程控制块), 典型的进程表项大致信息如下:
进程的切换都是一次中断,所有的中断都是从保存寄存器开始,对于当前进程而言,通常是保存在进程表项中, 中断的处理和调度过程如图:
2.1.7 多道程序设计模型
CPU利用率 = 1 - p^n. p为一个进程等待I/O操作的时间与其在内存中时间的比值,n为程序的数量。
2.2 线程
一个进程包含一个线程,对现在的系统而言,通常有多个线程。
2.2.1 线程的使用
多线程是在多进程的基础上继续的演化来的, 都是为了提升CPU的利用率,因为CPU是一种很宝贵的资源,因减少浪费。假设一个进程要执行三道数学题,如果没有线程的概念,那这个进程只能一道一道的去完成,执行第一道产生I/O操作时,就会阻塞,知道阻塞完成了才会继续执行。这样效率就比较低,而如果有了多个线程,每个线程执行一道数学题,互不干扰,这个线程就可以一直处于运行状态,抢到时间片的几率就大了,就可不断执行。并且,如果当前运行进程少或者其他进程也在阻塞,那没人用CPU,就是一种浪费,所以多线程可以尽量减少进程的阻塞,从而可以让CPU一直有事可做。当然,这只是一种比喻。 还有就是,进程切换的开销远大于线程切换的开销,毕竟,线程切换都是在同一个进程中。
2.2.2 经典的线程模型
进程间的线程不像同进程那样有很大独立性,所有线程有完全一样的地址空间,共享同样的全局变量,因此没有跨进程通信的麻烦。每个线程也有自己的程序计数器、寄存器、堆栈、转态等:
每个进程的数据、打开的资源、文件、代码都是相同的,每个线程都可以取用,但是每个线程都有寄存器、堆栈等。
2.2.3–2.2.5 用户线程和内核线程
用户线程:在用户空间实现线程,内核对线程一无所知, 内核依然按照老的方式,把进程当做单线程进程。
用户线程优缺点:
优点:
- 1、线程切换不用陷入内核,不需要上下文切换,所以线程切换速度很快;
- 2、允许每个进程有自己定制的调度算法。
缺点:
- 1、在如何实现阻塞系统调用上实现有困难,而如果采用非阻塞方案,需要修改原有的操作系统;
- 2、如果一个线程开始允许,那么该进程中其他线程就无法运行,除非第一个线程放弃CPU,那么线程的调度又是一个问题;
内核线程:在内核空间实现线程。
最优实现:用户线程和内核线程混合实现。
2.3 进程间通信(IPC,Inter Process Commutation)
进程间通信是很重要也很常用的的一个概念,主要围绕三个问题:
- 1、如何把信息传递给另一个进程
- 2、如何确保两个或多个进程不会交叉(例如多个用户在飞机订票系统同时取买票,该给谁)
- 3、如何确保按正确的顺序执行:例如B进程要打印A进程的结果,那么肯定是先执行完A,才能执行B。
2.3.1-2.3.2 竞争条件、 临界区
多个进程同时读写某些共享数据,就存在竞争。
多个进程同时需要访问的**【程序片段】称为临界区**。
为了避免竞争条件,引入互斥的概念,设计的方案应该满足以下四个条件:
- 1、任何两个进程不能同时处于临界区
- 2、与CPU的速度和数量无关
- 3、临界区外的进程不得阻塞其他进程
- 4、进程不能无限期等待
使临界区保持互斥的效果图如下:
2.3.3 互斥
首先明白两个概念, 同步与互斥
互斥:散步在不同进程的代码片段,当某个进程访问了其中一个片段时,此时其他进程就不能访问了,只能等该进程结束后才可访问。
同步:是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的 某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。显然,同步是一种更为复杂的互斥。
互斥的方案有:、屏蔽中断、锁变量、严格轮换发、Perterson解法、TSL指令。
屏蔽中断:效果太差,不会这么做
锁变量:理想化,软件难以实现
严格轮换法:需要用到忙等待,非常浪费CPU。导致其他进程可能被阻塞。 连续测试一个值直到某个值出现,称为忙等待, java示例代码如下:
while(true) {
while(a != 0) {
break;
}
}
如果a一直等于0,则无限循环下去。
Peterson解法:一个不需要严格轮换的软件互斥算法。
TSL指令:硬件支持的方案,需要用到TSL指令:
TSL RX, LOCK
TSL指令将 内存自LOCK读到寄存器RX中,然后在该内存地址上存在一个非零值。读字和写字由操作系统保证是不可分割的(原子性),CPU将锁住内存总线,以禁止其他CPU在本指令结束前访问内存。
一个可替代TSL的指令是XCHG,本质和TSL解决办法一致。
2.3.4 - 2.3.8 进程的睡眠与唤醒
Perterson解法和TSL/XCHG解法都是正确可行的,他们的本质都是:当一个进程想进入临界区,先检查是否允许进入,若不允许则原地等待,直到允许为止。 但很浪费时间。引入生产者消费者,但没从根本解决。
信号量:将检查值、修改值以及可能发生的睡眠操作作为一个单一的、不可分割的原子操作完成,原子性由操作系统保证。在完成前,其他进程不允许访问信号量。
互斥量:信号量的简化版本,不需要计数能力,只需要两种状态:解锁和加锁。一个二进制位就可表示它,不过通常用整型。
管程:是编程语言的组成部分,编译器知道他们的特殊性。任意时刻管程中只有一个活跃进程。 管程可以看做一个软件模块,它是将共享的变量和对于这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,进程可以调用管程来实现进程级别的并发控制。管程具有面向对象编程的特点。
额外概念,同步原语:保证同步执行的代码语句。大致理解为只有将同步的代码执行完毕,才能顺序执行下一段代码。
2.3.9屏障
通常用于进程组,把他们的执行划分了不同阶段,每个阶段末尾设置一个屏障,只有所有进程都到达屏障,才能继续运行下一个阶段。
2.4 调度
多个进程同时竞争CPU,应该选择哪一个进程执行,这就由操作系统的 调度程序完成,它内部实现了调度算法。
进程切换:占用CPU资源的使用者发生了切换。需要保存当前进程在PCB(进程控制块)的执行上下文(寄存器、数据、打开资源文件等),然后恢复下一个进程的上下文。
调度:从就绪进程队列中挑一个进程去CPU上执行。
何时调度:
- 非抢占式系统:调度算法挑一个去运行,直到该进程阻塞或自动释放CPU。
- 抢占式系统:雕塑算法挑一个进程,让其运行固定的最大时间周期,如果时间到了还在运行,则挂起等待下一次运行,然后切换下一个进程。
调度算法目标如下:
批处理系统中的调度算法:
- 1、先来先服务:按照请求CPU的顺序使用CPU。
- 2、最短作业优先:谁的运行时间短,谁先执行,好处是每个作业的平均等待时间短。如两个进程,A需要运行20分钟,B两分钟,如果先运行A,则B等待20分钟,总的等待20分钟,平均等待10分钟;如果先运行B,在运行A,则A等待2分钟,总的等待2分钟,平均等待一分钟。
- 3、最短剩余时间优先:也是抢占式的,每次找到剩余执行最短的程序执行,给其固定的运行时间,如果到期还没运行完毕,则进入就绪队列等待。
交互式系统的调度算法:
-
1、轮转调度:也就是大家轮流来,一个进程分配固定时间片,时间到了还没执行完,则移动到就绪队列队尾,下一个进程接着来。
-
2、优先级调度:优先级高的进程先运行,统一优先级的则按照轮转调度。:
-
3、多级队列:举个例子,高优先级的进程先运行一个时间片,然后是次高级队列每个进程运行2个时间片,然后再次一级运行四个时间片。每个进程运行一次后,优先级降低一级。
-
4、最短进程优先
-
5、保证调度
-
6、彩票调度
-
7、公平分享调度
实时系统的调度:硬实时调度:在绝对截止时间前完成,软实时调度:在某个时间前后完成调度。
**调度策略与机制分离:**将调度算法以某种形式参数化,具体参数由用户进程写入。调度机制位于内核,调度策略可由用户进程决定。
**线程调度:**与进程调度类似。