二、进程管理

  进程是操作系统实现并发的基础,了解进程才能明白操作系统运行的实现过程。这篇文章对于进程进行一个讨论,希望在读完之后能对进程有一个清晰的认识。

  进程概念

  对进程概念的介绍,围绕着这几个问题:
    1. 为什么要进程?
    2. 什么是进程?
    3. 进程由什么组成?

  1. 为什么要进程?
  多道程序环境下,程序并发执行。为了让各个程序相对独立,运行无误,引入进程这个概念来对并发执行的程序加以描述和控制(这个比较晦涩,从进程本身角度说,程序A运行的时候希望有一个独立的空间不受他人干扰,进程就提供了这个空间。从使用者的角度说,对操作系统引入进程这个数据结构,能够更清晰的表达动态系统运行的内在规律)
  2. 什么是进程?
  这个问题说法不一,但我觉得进程就是运行中的程序。这句话也侧面解释了进程和程序之间的区别。程序是静态的,而进程是动态的。除此之外,进程内部的内容也更多,将在下个问题中介绍。
  3. 进程由什么组成?
  我们常常说的进程其实是进程实体(进程映像) 的简称,主要有程序段、相关的数据段、和PCB(下面再详细介绍)三部分组成。这里的相关数据段包括堆、栈、数据段三类,程序段就是代码段。
下面详细介绍一下PCB(这可是进程里面很重要的!)
在介绍PCB之前说一下,为啥要PCB?都知道程序分时运行,到了时间就要换程序执行。那不同程序之间的状态肯定是不同的(程序A有程序A的数据,程序B有程序B的数据)。之前说过在两次时间切换之间,操作系统会实现保护现场和恢复现场。那他们的依据从哪来呢?就是PCB!所以PCB就是保存进程中各类数据信息及当前情况的数据结构
  进程控制块PCB(process control block)
  PCB中主要包含如下四部分
  1) 进程标识符
  进程标识符的作用就是唯一的标识一个进程,以便于对这个进程进行调用。有两种类型,针对于用户和系统使用,分别是外部标识符(用户),内部标识符。编程序时fork函数返回的应该是外部标识符。
  2) 处理机状态
  处理机状态又称处理机的上下文(还有一个概念是上下文切换,就是针对于这部分内容的切换),主要包括各种寄存器中的内容:通用寄存器、当前指令地址(PC)、程序状态字(PSW)、用户栈指针。这部分内容也是PCB存储中最显著的。
  3) 进程调度信息
  进程调度信息表明该进程当前的状态(关于进程状态的分类稍后介绍),进程的优先级、用于完成调度的其他信息(还有什么?)、如果发生阻塞,存储阻塞原因。学名叫事件。
  4) 进程控制信息
  指用于进程所必需的信息,包括本进程程序段和数据段的地址(不一定是首地址,可能是当前执行的)、进程同步和通信机制所需要的一些东西(都有哪些?),资源清单,包括总预计和当前已分配、链接指针,本进程PCB所在队列的下一个进程PCB的首地址(怎么理解?PCB之间的存储以链表形式吗?)
  如果一个进程一个PCB的话,PCB的数量也不会少。所以需要方式进行管理,目前常用的组织方式包括线性方式、链接方式、索引方式。在此不再细说,有需要可以自己去查。
  以上介绍的PCB内容都比较基础,但其实PCB是一个很大的数据结构。涉及很多方面。更为详细的说明如下图(我还没学到,就先不一一介绍了):
在这里插入图片描述
  下面来补上面的坑,谈谈什么是进程状态。
  进程最基本的状态有三种:就绪、阻塞、执行
  就绪指进程已经准备好了,只要获得CPU的资源就可以运行程序。因为处于就绪状态的进程可能很多,所以以队列存储,并配合专门的调度算法。
  执行就是进程正在进行,没啥好说的。对于单核CPU,同一时间只有一个进程处于执行状态。
  阻塞指执行状态的进程因某些操作(通常是I/O)而终止运行,进行等待。阻塞完成后会重新进入就绪状态。
  执行状态会有两种变化方向:一种向就绪状态(时间片到达),一种向阻塞状态(发生I/O操作)。三者转化关系如下图(图中引入新的,终止状态是为了使结构完整):
在这里插入图片描述
  我们常常听到一个名词叫”挂起”,比如后台进程的挂起。挂起也是一种改变进程状态的操作。那为啥要挂起呢?原因有很多,比如这个进程不用,通过挂起可以将这部分资源拿出来给其他的用。或者是通过挂起完成对进程的控制(不让运行了)。当引入挂起操作后,就区分出活动静止两个分类(活动就绪,活动阻塞,静止就绪,静止阻塞)。普通处于就绪状态就叫活动就绪,挂起后就变成了静止就绪,普通处于阻塞状态就叫活动阻塞,挂起后就变成了静止阻塞。与挂起相对的是激活操作,实现反向功能。当引入创建状态的时候,从创建状态可以直接变成就绪状态。如果进程在内存中变成活动就绪,在磁盘中变成静止就绪(远的就是不用,就静止)。把所有的状态都放在一张图里就是下面这个样子:
在这里插入图片描述

(老师说,考试的第一题就是关于这些状态的转换,求求千万不要错~)

  进程通信

  带着问题说:
    1. 为什么需要进程通信?
    2. 有哪几种方式实现进程通信?

  1. 为什么需要进程通信
  关于这个问题,老师给了三种原因:
  1) 当无法改写应用程序时,用进程通信去操作已有的应用程序(怎么理解?)
  2) 完成同一个任务的多个进程需要通信。
  3) 模块化、方便(怎么理解?)
  实现进程通信的最大目的就是数据共享,操作系统分时特点,同一程序多个进程间相互协作说白了也是为了实现数据共享。不过这里面的进程通信指的是进程间,而不是进程内部。

  2. 有哪几种方式实现进程通信呢?
说到通信,最容易想的方式肯定是通过文件。一个写一个读,只要处理好顺序问题就不会有错。但文件读写也属于I/O操作,实在太慢。所以操作系统额外提供了三种进程通信的机制:消息队列、共享内存、管道
  1) 消息队列
  实际上用链表维护了一个消息队列,进程具有读或写的权力。发方写,再让收方 读。很类似于文件,但可能存放的位置不同,使用起来更快。但这里存在一个矛盾,
  队列中每个数据多大呢?选择定长的话易设计,但不易使用。不定长相反。所以基本只用于早期系统。有关于消息传递的编程可以参考下面这个博客:进程通信之消息队列_我爱加菲猫-CSDN博客_进程通信消息队列;
  2) 共享内存
  共享内存就更是简单粗暴了,既然希望共享数据,直接在内核中开辟一段空间作为共享数据区域,所有进程都可以读写。因为是在内核区域,所以存取的速度比较快。也是实现通信的最快方式。不过哪有这么好的事儿?访问同一区域就会遇到数相关问题(先读后写),所以共享内存的使用建立在进程同步的基础之上。
  3) 管道
  消息队列太慢,共享内存没有规定容易乱。管道就起一个权衡作用,集百家之所长。在内核中开辟一段空间,以队列方式管理,管道一端只能输入、一段只能输出。但管道空间有限,所以要做一些判断:满了不能写,空了不能读。管道还涉及到一些重定向的问题,我现在也不会,就不说了(美其名曰:扬长避短)。在目前的系统中,管道是用的最多的一种。

  线程

  线程这个概念,应该听过很多次了吧。在强调并行计算的时候,都会说到多线程。那线程和进程间有什么联系呢?简单的总结:线程是进程的一个变种。下面来聊聊吧!

  1. 为什么需要线程?
  咱们首先来比较一下,一个程序的多个进程间有什么异同?
  相同之处:代码、数据一样,权限一样、文件资源一样
  不同之处:每个进程有各自的执行状态,PC等寄存器。
  可以把相同不同分为两个类:资源、执行状态。资源都相同,还为啥要每个都存一份。只要保存每个进程各自的执行状态就行了呀!对喽,这就是线程的思想。把执行状态取出来,就叫做线程(感觉有点儿像那个提取公因式,hhh)。
  下面来多说一点儿和线程相关的:
  之前认为进程是操作系统中最小的,但其实不是滴。线程才是处理器调度的基本单位。一个进程可以创建多个线程,多个线程共享地址空间、资源,线程也有三个状态(就绪、执行、阻塞),操作系统怎么感知线程呢?和进程一样,进程有PCB,线程有TCB(线程控制块, Thread Control Block)。每一个线程都要有一个独立的栈(共用一个会满滴,所以不行)
  来比较下线程和进程,它们各自都有什么优势呢?
  线程更快、更小。但独立性不好(需要共用资源)。进程就反过来了。所以不同不同需求选择就不同,而不是说线程一定比进程要好。举个例子网页浏览器多个网页用的是进程,而不是线程。为什么呢?这就是线程一个最大的(I think)缺点:一个进程中有多个线程,但若一个线程出错,整个进程被强迫终止。以线程管理网页就会一个崩,个个崩。所以多线程编程还是多进程编程也是看需要。
  以上常说的线程都叫做内核级线程,是由操作系统内核建立的。但有一个概念要清楚,涉及到操作系统内核的操作必将需要状态的转换(用户态到内核态),这个是要花时间的。所以能在用户态做的事情是最好的了。所以又设计了用户级线程,管理模块叫做utcb,分配在数据段,同时也要有独立的栈。因为没有操作系统参与,但线程仍需要调度。所以需要自己在代码段完成调度算法。优点就是性能高,灵活(允许进程制定属于自己的调度算法,进程管理灵活)。拿用户线程和内核线程做一个比较,如下图:
在这里插入图片描述
  系统调用阻塞问题指用户级线程如果调用了系统调用,就会引发系统调用阻塞。(可能是没权利吧)。更详细的比较可以参考:用户级线程和内核级线程的区别 - 知乎 (zhihu.com)
  用户级线程的实现方式有多种(依旧依附于内核级线程)。分为多对一(多用户对应一内核),一对一(一用户对应一内核)、多对多(多用户对应多内核)。这里了解的不是很多,就不说了。

  进程同步

  首先思考一个问题:为什么需要进程同步?试想这样一个场景,对于一个全局变量,很多个进程都可以访问并且对该变量进行操作。结合计算机分时系统的特点,那会不会出现不同情况下执行结果不一致的情况。答案是会!(原因在此不做解释,自己去查查或许印象更深)我们称这种全局变量为临界资源,针对于代码中对于临界资源的操作过程称为临界区,针对于临界资源产生的问题叫做临界区问题
  所以我们需要进程同步机制来控制各个进程对于临界区的使用。书上对此的描述原文是:进程同步机制的主要任务,是对多个相关进程在执行次序上进行协调,使并发执行的诸进程之间按照一定规则(或时序)共享系统资源,并能很好地相互合作,从而使程序的执行具有可再现性。
  进程同步对于临界区问题的解决有四个原则:空闲让进、忙则等待、有限等待、让权等待。我用自己的理解简单解释下:
  空闲让进:如果临界区空闲,任何一个需要临界区的进程都可以使用
  忙则等待:自己想要使用临界区,但此时此刻被占用,要排队等待,空了再用
  有限等待:等待也是有限度的,要保证进程能够使用到临界区
  让权等待:如果已经知道没希望了,赶紧换个进程,别占用等待区的位置
  进程同步只是个机制,那具体可以通过什么方法呢?老师和书一共给了三种方法:Peterson方法、硬件同步机制、信号量机制。下面一一展示下(在此不做详细的说明,每一种方法展开说都太多,有需要的再去找对应的内容即可):

  Peterson方法:

在这里插入图片描述
  Peterson方法通过增加turn变量结合flag实现了进程同步机制(对于临界区的唯一使用,如果冲突会卡在while处)。它的优缺点如下图:
在这里插入图片描述

  硬件同步机制

  之所以会发生临界区问题,就是因为分时复用嘛。分时复用是操作系统按照时钟中断进程调度。如果硬件上在每次进入临界区代码前,直接关闭中断。就不会有时钟,也没了分时,也不会有任何冲突问题。这就是硬件同步机制的主要思想。如果用锁来描述的话,关中断就是关锁,开中断就是开锁,判断锁的状态叫测试。为了防止多个进程同时测试到锁打开的情况,测试和关锁必须是连续的
  硬件同步的具体落实是通过硬件指令实现的,具体的有两种:test-and-set、swap。(具体的不说了)

  信号量机制

  信号量其实也经历了一段发展的过程,基本顺序可以分成:整型信号量、记录型信号量、AND型信号量、信号量集,依次介绍下

  整型信号量:

  所谓整形信号量就是定义一个整型量表示资源的使用情况,只有两个操作wait()和signal()能够访问。也对应的称wait()为P操作,signal()为V操作。
  两个操作的实现如下(简单描述):

	Wait() {
	While (s <= 0) do no-op
	s--;
}
	Signal() {
	S++;
}

  这个例子中S就是信号量,初始化为某一正整数n,代表资源的数目。当资源的数目大于0时(有可用的资源)执行资源自减,然后去做某一处理。当资源为0时会一直卡在while处等待,直至某一进程结束运行后归还资源再进行分配。通过这种方式,我们就好像建立了一堵墙,拿到钥匙(资源)才能够访问临界区。
  想想这种方法有什么缺点呢?如果按照这个思路的话,拿不到资源的进程会一直卡在while处,什么也不做,那这个时间片的各种资源就相当于浪费了。把这种现象称为“忙等”。为了消除忙等,引入了记录型信号量。

  记录型信号量

  这种方法是解决了忙等问题。运用的思想是通过改变进程的状态。之前说过阻塞状态的进程是不占据资源的。所有如果进程执行到指定位置但没有资源可用,就改变进程状态为阻塞状态。同时用链表这种结构对所有阻塞进程进行保存。对于二者的代码描述如下:
在这里插入图片描述
  之前说资源可以有很多个,当只有一个的时候。这时候的信号量叫做互斥信号量。
  使用记录型信号量帮助我们解决了忙等问题,但再想一下,当面临多个信号量时wait()的顺序是否会影响呢?答案是肯定的!看下面这个:
在这里插入图片描述
  如果这个交错执行的话,就发现死锁了。引起问题的原因就是顺序不对,那要是能一起执行就好了,这就是AND型信号量!
在这里插入图片描述
  对于信号量集就是为了模型更一般化,在这里不进行详细讨论了。平时用的感觉也少。
  对于信号量(PV操作)更重要的在应用上,这篇文章主要是一些概念。后面会有一篇专门研究PV操作的几个典型例题,去了解如何使用PV操作。
  进程这部分到这里也就写完了,但感觉还远没有领悟到精髓,后面再继续学习吧!

因作者水平有限,如果有错误之处,请在下方评论区指正,谢谢!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值