计算机操作系统(第四版)第二章-进程管理

一、进程的定义

1 什么是进程?

简单的说,进程就是程序的一次执行。一个程序可以对应多个进程,比如一个程序被多次运行,就会对应多个进程。

   为什么要引入进程呢?直接使用程序的概念不行吗?

(1)进程是一个动态的概念,而程序是一个静态的概念。可以这么理解:程序是指令的有序集合,没有任何执行的意义。但是进程不一样,进程被动态的创建,被调度之后又消亡。

(2)进程具有并发(并发不是并行,第一章已经说过)特征,而程序没有。为什么说程序没有这个特性,因为程序不反映执行过程,就是一堆指令(代码)。

(3)另外就是进程是计算机资源竞争的基本单位,所以其并发性受到系统自己的制约。

二、进程状态

1 一般来说,通常有五种状态:

(1)新建:进程正在被创建。

(2)就绪:进程可以被运行,正等待获得处理机。(进程在就绪队列中,等待进程调度程序的调度)

(3)运行:进程中的指令正在被执行。

(4)阻塞、等待:进程因为等待某事件(比如请求I/O)而暂停执行。

(5)完成:进程执行完毕、结束。

有些操作系统也具有一些其它状态,并不完全一样。比如操作系统实验中就会有游离状态,其实就是中间态。

   为什么需要新建状态(NEW)和完成状态(DONE)?

        一般来说,操作系统创建一个进程需要两步:第一步先创建一个进程,第二步将其送入就绪队列,之后才有可能被调度执行。

        同样的,一个已经结束的进程退出操作系统也分为两步:第一步操作系统先将该进程变成一个不能再运行的状态,也就是done。但是第二步操作系统并不会立即撤销它,而是将其暂留在内存中,让其他进程去收集相关信息,以便提高性能。

2 状态转换图

   进程可以直接从就绪->阻塞吗,可以从阻塞->运行吗?

(1)就绪->阻塞应该不行,我是这么理解的:从阻塞的定义上看,阻塞一般是指进程因为等待某事件而暂停执行,如果一个进程还在就绪状态,它就根本不处于运行状态,又何谈暂停执行一说。

(2)阻塞->运行应该是可以的,我是这么理解的:如果一个进程等待的事件已经发生,而且就绪队列中如果不存在其他进程,那么从设计的角度来看,这个进程应该是可以直接被投入运行的。

3 七状态进程模型

        由于内存空间有限,如果某一时刻进程数量过多,不可能全部在内存中吧,这个很好理解。所以有些操作系统为了缓和内存紧张的情况,引入了挂起状态。也就是将就绪分成了活动就绪状态(没有被挂起的就绪进程)、静止就绪状态(已经被挂起的就绪进程)。将阻塞分成了活动阻塞状态(没有被挂起)和静止阻塞状态(被挂起)。

        那麽挂起和没有挂起有什么区别呢?挂起就是把进程挪到了外存中,因为我们引入挂起的初衷就是缓和内存紧张。因此可以引入以下概念:

        就绪:进程在内存中,只要被选中就可以立即运行。

        就绪挂起:进程在外存中,只要进入内存,就可以立即运行。

        阻塞:进程在内存中等待某事件发生。

        阻塞挂起:进程在外存中等待某事件发生。

        挂起:将一个进程从内存移动到外存。

        激活:将一个进程从外存移动到内存。

需要考虑的几个问题:

(1)为什么要引入挂起?

        前文说的很清楚,引入挂起就是解决内存不足的问题。

(2)何时挂起?

        内存不足时候,达到阈值。(发现内存不足或者预期内存不足,显而易见的道理,你不可能等到一点内存没有再挂起。肯定要设定一个上限)

(3)选择什么进程挂起?

        肯定选择暂时一段时间内不需要执行,或者优先级低的(你要到外存中,调进来又要浪费时间)

(4)何时激活进程,选择什么进程激活?

        选择优先级高的,立马可以投入运行的。

状态转换图:

同样的也存在以下激活状态(图中没有画出):

        就绪挂起->就绪:就绪队列中已经没有进程,或者就绪挂起进程的优先级比就绪队列进程更高。

        阻塞挂起->阻塞:当内存足够,比如有些进程已经完成了执行,结束之后需要撤销相关资源,所以此时可能会将阻塞挂起进程挪入内存。

三、进程的描述

1 进程控制块PCB

  1. OS管理和控制进程的数据结构
  2. PCB记录着进程的描述信息
  3. 每个进程对应一个PCB,(也就是说一个程序可以对应多个进程->多个PCB)

2 PCB的作用

(1)PCB本身就是进程的一部分,进程由三部分组成,分别是程序、数据、PCB。为什么这么说?程序和数据很好理解,因为进程本身就是程序的一次执行。而PCB包含着进程的描述信息(PID、优先级等等)。

(2)PCB伴随着进程的整个生命周期。

        进程创建的时候,由OS创建PCB,进程结束的时候,由OS撤销PCB。进程运行的时候,PCB作为调度的依据(优先级、状态、时间片等等)

3 PCB中的主要信息

(1)进程本身的标识信息

        进程标识符(PID):OS分配,整数,唯一标识进程。相当于数据库中的主键。

        用户标识符(UID):标识进程是由哪个用户创建的。

         对应程序的地址:在内存还是在外存。

(2)CPU现场

        进程上下文切换所需要,或者中断处理的时候,CPU执行另外一个中断程序,执行完毕之后回到断点处继续执行,能够回来的前提就是保留了CPU现场。

(3)进程的调度信息

主要是可以根据进程依托什么来调度推导:

        进程的状态(比如进程处于就绪状态就可以调度运行),优先级,使进程阻塞的条件(如果PCB不记录阻塞进程等待的事件,那么该事件发生了进程仍然不知道,会一直处于阻塞),占用CPU、等待CPU的时间(这个和时间片轮转、进程调度有关)

(3)进程占用资源的信息

        为了实现存储保护,资源共享的时候需要确保进程不会影响其它进程。跟互斥、同步有关。

4 PCB的组织方式

   一般通过PCB表来把PCB组织起来,放在内存的特定区域。PCB表的大小决定了进程的数量,这是因为你要创建进程就需要PCB。

下面是两种组织方式:

四、进程同步

1 多进程并发执行

一个进程执行没有结束,另外一个进程就已经开始执行了。

2 临界资源和临界区

  1. 临界资源是指必须互斥访问的资源。
  2. 临界区是指进程中访问临界资源的那段程序(进程由程序、数据、PCB三部分组成)

互斥访问是什么意思?互斥访问就是说多个进程不能同时访问,其实也是一种特殊的同步。

举个例子,下面这个打印事件就没有处理好互斥(红色是临界区):

AB同时指向一个空单元,但是A写完之后,B还没写,B此时会覆盖

3 互斥方案所需要具备的条件

  1. 任何两个进程不能同时处于临界区(互斥本身就是这么定义的)
  2. 临界区外的进程不应该阻止其他进程进入临界区。
  3. 不应该使得进程在临界区外无休止的等待,也就是说,进入临界区的进程代码执行要快。
  4. 不应对CPU个数和进程之间的相对运行速度做任何假设。

4 互斥方案的实现

详见 忙等待实现互斥方案-CSDN博客

信号量的实现(忙等待+阻塞)-CSDN博客注意:上面的原子机器指令都是通过忙等待的方式实现的。忙等待是指什么:当一个进程想进入临界区的时候,先检测。如果不允许进入,就不断检测,直到允许为止。

忙等待存在什么问题?

  1. 浪费CPU时间。
  2. 可能引起优先级反转问题。举个简单的例子:假设某时刻存在

两个进程A和B,其中A的优先级比较高。按照常理来说,会优先调用A。但是如果B在临界区内,而A此时刚刚就绪。由于调度程序选中A,但是A无法进入临界区(缺少资源),因为B没有出来,所以A无法投入运行。

五、信号量的引入

信号量是OS引入的解决同步和互斥问题的机制。它的取值是一个

整数。

1 信号量的使用

  1. 必须设置初始值并且只能设置一次
  2. 不能对信号量做除PV操作之外的任何操作。
  3. 信号量表示可用的资源数量。

2 P操作和V操作

假设存在一个信号量S。

  1. P(s):表示请求分配一个资源,因此s-1,当s<=0的时候说明没有可用资源,必须等待其它进程释放之后才能运行。
  2. V(s):释放一个资源。

3 信号量的实现原理(前文的互斥是通过什么实现的?中断和原子操作,但是中断一般只适用于单CPU,多处理器一般通过原子机器指令来实现)

  1. 忙等待
  2. 阻塞

六、进程间通信

1 进程通信的类型

(1)共享内存

        相互通信的进程之间设置有公共的内存区,每个进程既可以向公共内存中写,也可以从公共内存中读。通过这种方式实现信息交换。

(共享内存属于临界资源,应用程序访问共享内存的时候,需要考虑同步互斥)

(2) 消息传递

        消息传递指的是源进程发送消息,目的进程接收消息。所谓消息就是一组数据。提供两种原语send()和receive()

        -直接通信:明确指定通信的接收者和发送者。

        Send(p,message):向进程p发送message

        Receive(p,message):从进程p接收message

        -间接通信:发送进程将消息发送到内核中的一个消息队列。接收进程从这个消息队列中接收消息。

        Send(q,mesage):向消息队列发送message

        Receive(q,message):从消息队列接收message

(3)管道

2 消息队列

(1)send(QID qid,MSG message)

        阻塞:消息队列满了就阻塞。

        非阻塞:消息队列没满就不阻塞。

(2)Receive(QID qid,MSG message)

        阻塞:消息队列空,等待

        非阻塞:消息队列空,立即返回

七、线程

1 为什么要引入线程?

        解决这个问题之前先想一下为什么需要引入进程这个概念,引入进程就是为了解决内存中多任务的需要,提高CPU的效率。

        但是现在为什么又要引入线程?必然是有一些进程存在的问题没有得到很好的解决。比如说,以多进程的方式解决多任务的时候,会存在如下问题:

  1. 进程的上下文切换耗时,浪费CPU时间。
  2. 而且还需要引入信号量、原子操作等来解决共享等问题。

引入线程的好处:

        (1)性能。将一个多任务应用实现为单进程、多线程可能比实现为多进程效率更高。因为创建进程比创建线程慢。

        (2)程序设计。资源共享更容易处理。因为一个进程内的多个线程可以共享进程的资源(全局变量、打开的文件等),便于任务之间的通信。

2 什么是线程?

  1. 线程是进程的一条执行路径。
  2. 1个进程可以有多个线程,但是至少有一个主线程。
  3. 1个进程内的多个线程在同一个地址空间内(共享该进程的地址空间)
  4. 每个线程都有自己的线程控制块TCB。包含自己的栈和状态信息。TCB比PCB小得多,这个很好理解,因为一个进程会包含多个线程。

3 线程的实现机制

(1)用户级线程(比如c++的thread库)

        由在用户空间执行的线程库来实现,OS对此一无所知。线程库提供线程创建、撤销、上下文切换等功能。这种类型的线程只要有线程库的支持,就可以运行在任何的OS上。

        在这种情况下:

        -资源分配的实体是进程。

        -OS分配CPU时间的基本单位进程。

        -线程的调度只进行线程上下文切换,而且在用户态下。

(2)核心级线程

        OS内核提供对线程的支持,系统调用API(线程创建、撤销等等)

        在这种情况下:

        -资源分配的实体是进程

        -OS分配CPU时间的基本单位是线程。

        -线程的调度在核心态下。

(3)上述两种线程实现机制的比较:

        同一进程内的多个线程是否可以在多个处理机上并行执行:

        用户级别线程是不能的,核心级线程是可以的。

        统一进程内的线程切换性能比较:
        用户级线程性能高,无需陷入内核。但是核心级线程性能低,需要陷入内核。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值