【认识操作系统中处理机管理功能,并利用信号量解决资源的互斥访问、同步问题】

1.前驱图

前驱图,用节点表示操作,可以理解成一个进程。节点间的箭头线,代表两个节点(进程)的先后关系(同步)

前驱图的作用,用于描述进程之间的关系。我们后面会经常用到前驱图这个工具。

2.单道环境下,程序运行的特征
  1. 资源独占性 (封闭性)
    任何时候,位于内存中的程序可以使用系统中的一切资源,不可能有其他程序与之竞争;
    封闭性指的是,程序的运行,在一个封闭的环境下运行的,不会被别的程序干扰。只有一个进程。独占系统资源,在操作系统中,进程是占有资源的最小单位(线程可以访问其所在进程内的所有资源,但线程本身并不占有资源或仅仅占有一点必须资源)。
  2. 执行的顺序性
    内存中只有一个程序,各个程序是按次序执行的。在做完一个程序的过程中,不可能夹杂进另一个程序执行;
  3. 结果的可再现性
    只要执行环境和初始条件相同,重复执行一个程序,获得的结果总是一样的;
    一个正确的程序,逻辑上,应该是这样。
    如果我们输入的初始条件一样,那么结果应该是一样的。
    比如,加法程序。无论我们运行多少次,只要初始条件是1+1,那么结果应该就是等于二,这就是可再现的含义,否则就称为不可再现。
  4. 运行结果的无关性
    程序的运行结果与程序执行的速度无关。系统中的作业以串行的方式被处理,无法提高CPU、内存的利用率;
3.程序并发执行的特征,待解决的问题

①首先理解什么是并发运行:
用自己的话来说,并发执行就是,多个程序一起进入内容,然后被等待调度执行,某一个时刻,只有其中一个进程占用cpu----真正的运行,但是,给用户的感觉,好像这一组程序是一起运行的(有时间间隔,宏观上同时进行的)。并发,就是同一个时间段(时间间隔)内发生的。

②多道程序一起进入内存运行,会出现什么问题呢?也就是,并发执行的时候,会有什么特征?
1.间断性:程序运行呈现出间断的特征。比如说,一个程序运行,然后需要输入输出,这时,就要让出cpu给别的进程,等完成输入输出,再继续等待被调度运行。整个运行中,走走停停,
程序并发执行时,由于它们共享系统资源,以及为完成同一项任务而相互合作,致使在这些并发执行的程序之间形成了互相制约的关系:并发程序具有“执行–暂停–执行”的间断性的活动规律。
2.失去封闭性 根本原因,就是多道程序,共享系统资源,导致存在资源竞争的问题,所以失去了封闭性,就是一个程序,会被别的进程影响。当系统中存在着多个可以并发执行的程序时,系统中的各种资源将为他们共享,而这些资源的状态也由这些程序来改变,致使其中任…
不可再现性: 因为第二个特征,就会导致第三个特征。两个循环程序A和B,它们共享一个变量N。
在这里插入图片描述
通过这个例子来体会,为啥会失去可再现性?研究了,并发执行可能会出现不可再现的情况;总结了,根本原因就是并发进程对共享资源的同时访问。所以,解决不可再现问题的关键在于,对共享资源,不允许并发进程同时访问。
A,B两个进程,并发执行,也就是这两个程序一起进入内存,根据异步执行的特征,操作系统,可能先调度A运行,再调度B,也可能先调度b先运行,再调度A,也有可能,A执行的过程中,可能因为什么原因停下来了,比如,时间片用完了,它必须让出cpu。也就是执行了一半,停下来,又执行B,B执行完,重新调度A,继续运行,所以A、B并发执行,存在几种情况。
①第一种情况。操作系统先调度A进程,再调度B
也就是先执行A里面的,N=N+1.
在执行B语句,print N,N=0;
这种调度情况,执行结果为

②第二种情况,操作系统先调度B,再调度A运行。
也就是先执行 print N,N=0,在执行A里面的,N=N+1;
这种调度情况,执行结果为
③第三种情况,先调度B运行,运行到一半,可能是因为时间片等原因,B让出cpu,调度A运行,A运行完后,在调度B继续运行这种情况,运行结果为也就是说,A,B两个进程,一起进入内存运行。可能的结果有三种,这就是不可再现。
不可再现肯定是不行的,比如,1+1,逻辑上,结果永远都是2,所以,不可再现的问题,是我们并发执行要解决的问题,必须可再现,要解决这个问题,要搞清楚原因,什么导致的不可再现?
根本原因在于资源的共享,我们上面的这个例子,共享的资源,就是变量N对共享资源的同时访问导致程序的不可再现,所以,解决这个问题的关键在于:不允许并发进程同时访问共享资源,也就是资源的互斥访问,就是解决这一个问题的,这种互斥资源,现实中很多,比如说大学的大部分教室,是全校共享的,但是某一个时刻,只能一个班使用,否则就出问题了;再比如,铁路也可能是共享的,但是,同一个时刻只能一列火车运行,否则,问题就严重了。讲了这么多。就是通过这个例子来理解,为什么并发执行会出现不可再现。

4.进程

进程的概念,主要体现运行这个特征,进程是有结构的,由程序、数据以及进程控制块(PCB),三个部分组成,进程控制块,进程所有的信息,都记录在进程控制块PCB中的,它是操作系统感知进程的唯一标识,所以这个数据结构很重要,一个进程对应一个PCB,创建进行的时候,有 一个很重要的工作,就是分配空白的PCB,然后对PCB初始化,其他特征就不解释了,并发,异步,前面解释过了。
在这里插入图片描述

这个图,关键的地方,描述了,进程状态是如何切换的,比如,在这里插入图片描述
阻塞没有指向执行的箭头,说明,不可能从阻塞直接切换到运行,进程的运行,是由操作系统根据当时的情况调度运行的,所以,阻塞完成后,只能切换到就绪,等待。进程运行过程中,可能因为要等待某个事件的发生,就会进入阻塞状态。典型的情况就是等待输入输出(请求I/O)。阻塞状态,当等待的事件发生,不会直接切换到运行,而是进入就绪,等待再次被调度运行。

我们通过前面的例子,明白了,对临界资源的同时访问,导致并发执行的程序出现不可再现的问题。解决这个问题的关键在于对临界资源应该互斥访问。现代操作系统普遍采用信号量机制来实现进程的互斥访问临界资源;利用信号量来实现进程间的同步。

5.利用信号量机制解决资源的互斥访问

一、
如果多个进程间存在时序关系,需要协同工作以完成一项任务,则成为同步(如输入进程和计算进程);如果不满足协同的条件,而知识因为共享具有排他性资源时所产生的关系称为互斥(如A、B两个进程同时申请同一台打印机)
用信号量实现互斥,互斥是并发进程之间由于共享资源而形成的间接制约关系,互斥需要调解两者使用资源的速度;
先讨论进程间的两种制约关系:间接制约,直接制约;
间接制约,是因为资源共享导致的,比如,教室全校都可以用,但是,如果有别的班级在用,我们就必须等,被制约了;
第二种制约关系,直接制约,这里,就是我们前面讲的同步问题,也就是说,并发进程之间存在着先后关系,比如说,c语言学习就是数据结构(C语言编写版)学习的前驱,数据结构为后继,这个事情,顺序不能错,这就是直接制约,其实就是同步问题;

这里,提了几个概念,解释一下:
1.临界资源:不允许同时访问的,叫临界资源;
2.临界区:访问临界资源的那段代码,叫临界区,对临界资源的同时访问,导致了不可再现的问题
二、
下面,我们来研究怎么解决临界资源的同时访问的问题
这个问题,现实中有很多例子,比如,最容易想到的办法就是加锁,比如商场里公用的放物品的箱子(存储柜),如果没人用,那我就可以用,然后上锁,别人就不能用了,用完之后,再解锁,别人就可以用了,这个是解决同时访问的常见方法。所以,我们写程序,访问临界资源的代码,通常是,先看看有别的进程在临界区吗?没有人用的话,先上锁,再访问临界资源。访问完,记得解锁,不然,就永远被锁了,大家都用不了,所以,加锁解锁都是成对出现的。
在这里插入图片描述
就是这种格式,进入的工作,通常包含检查,加锁等,退出的工作,主要是解锁,这样就避免了同时访问临界资源的问题,就不会出现不可再现现象。提醒下自己:
既然写下了这个问题,以后对临界资源要非常敏感,访问共享资源了,条件反射似的要考虑到同时访问的问题,不然,程序肯定会出错了,现代操作系统普遍采用信号量机制来实现互斥、同步问题。
用信号量来解决互斥访问问题,得先学习信号量的概念

整型信号量:

最初由Dijkstra把信号量定义为一个整型量,除初始化外,仅能通过两个标准的原子操作wait(S)和signal(S)来访问。这两个操作一直被分别称为P、V操作。
仔细看这个定义,信号量和普通整形变量的区别在哪?
信号量的含义,通常用于表示资源的数目,跟普通整型量最大的区别在于,不可以对信号量直接访问,只能通过,不允许直接访问、操作信号量,只能用两个标准的原子操作来进行,wait(s)、signal(s)。
先来研究wait(s)的内部代码,
在这里插入图片描述
一个循环,如果信号量小于等于0,就退不出循环,被阻塞在这,这个条件,可以看到,信号量小于等于0,说明没有资源,被阻塞等待,一直到条件成立,条件成立,那信号量大于0,说明有资源了,可以从循环退出,往下运行,里面的操作,就是把信号量减一,相当于申请到一个资源,资源少了一个,这个是wait操作的内部情况,我们可以把wait操作理解成,申请资源,没有资源,就被阻塞等待,本质是在while循环那里,空转,有资源,那么资源数减一,可以理解成加锁。
在这里插入图片描述
可以理解成释放资源,里面代码,将资源数加一,这一对操作很重要,要重视。
熟练利用信号量解决互斥问题的步骤:
在这里插入图片描述
写程序的时候,要敏感,哪些是互斥资源,如果是互斥资源,一定要确保互斥访问,具体方法,一类资源,定义一个互斥信号量,初值设为1,访问资源之前,对互斥信号量做一次wait操作,访问完,做一次signal操作,注意wait和signal成对出现。
看一下下面的图片(思考题可不用关注,也不完整。)
在这里插入图片描述
解决进程互斥,可以按上面的例子来写,基本都一样,在这里插入图片描述
前面做一次wait操作访问临界资源,访问完做一次signal,相当于释放资源,不然,就永远被锁住了,别的进程进不了临界区。
如果我们按照这个套路写,有没有可能出现并发进程同时进入临界区的情况?
在这里插入图片描述
这个例子,两个并发进程,有没有可能两个进程同时进入临界区呢?实际上,是不可能的,比如,操作系统,先调度p1(process1简写,表示进程1,下同),p1进入临界区前,首先对互斥信号量做wait(上锁)操作,mutex就变为0了,假设,这时p2想进入,实际上是进入不了的,因为,首先要做wait操作,mutex=0,无法从wait操作返回,被阻塞到wait这里等待,不能进入临界区,也就不会对互斥资源进行同时访问。所以,只要是临界资源,我们就按这个套路写:
第一步,设置互斥信号量,初值为1;第二步,进入前,对互斥信号量做一次wait;第三,出临界区的时候记得做一次signal,确保wait和signal成对出现,这样就能确保,对临界资源的互斥访问,也就解决了,并发程序执行的不可在现的问题。

通过学习可以知道,除了整型信号量,还有记录型信号量,为什么有了简单信号量,还要弄一个记录型信号量?因为简单信号量,违背了同步的几个原则的其中一个。先上同步机制应遵循的规则:
1.空闲则入:其他进程均不处于临界区;
2.忙则等待:已有进程处于其他临界区;
3.有限等待:等待进入临界区的进程不能“死等”;
4.让权等待:不能进入临界区的进程,应释放CPU。
主要违背了第一条:空闲则入:其他进程均不处于临界区;什么意思?也就是一个进程不能进入临界区的时候,要让出cpu,不能占着cpu什么都不干。
我们来仔细研究一下:
在这里插入图片描述
当S小于等于0,实际上,一直在做检测工作, 在while循环那里空转,别的进程想运行,就不行,违背了上述原则,这种写程序的方式也不好,举一个之前的例子,比如,我们要开会,要去教室开,教室是共享资源(了解大学的特点都懂),我们一去,发现有人在用,就只能在那里等。。。这浪费了我们宝贵的时间,本来,我们可以干很多别的事情按这种写法,就是不停的看,教室是不是空闲了?干这事去了,实际上,一个比较好的方法是,我们去申请教室,如果有人用,那么,我可以先登记一下班级信息,说明我们要用,等别人用完了,发一个信号过来,告诉所有等待这个教室的班级,然后,大家继续抢教室,这种的好处是,不用总是检测,我们可以干别的事情,收到空闲通知,我们再去抢,这里道理是一样的。
提出了第二个信号量,
在这里插入图片描述
有两个数据项,value,相当于原来的s资源数,不同的地方在于多了一个链表,L:list of process; 这个链表干嘛用的?把所有申请该资源的进程串在一个链表里。这样的好处在于,等资源空闲了,只需要给这个链表的所有进程发一个信息,然后,这组进程就可以继续抢资源。
在这里插入图片描述
看wait操作,有什么不同:先减一,先申请,
在这里插入图片描述
如果资源小于0,那么意味着资源被别人抢走,那么,就把申请该资源的进程串链表里,可以干别的事情,等通知,大家对比一下简单信号量的wait,前者,是不断的检测,这里是,相当于申请了,就干别的事情,等通知,解决了让权等待问题。

这里提一下S.value的含义:
1.
在这里插入图片描述
2.
在这里插入图片描述
下面,认识第三种类型的信号量,AND型信号量,为什么提出第三种类型的信号量,因为,实际中,申请的资源类型往往不是一种,而是多种,为了方便而提出。这种信号量的思想:在这里插入图片描述
为什么会有这种思想,这个思想,最重要的一条,“要么全部分配到进程,要么一个也不分配”,为了帮助理解,举一个例子,假设有两个并发执行的进程,A,B,这两个进程,都需要申请资源D,E,那么,进程可能是这样:在这里插入图片描述
先看进程A(process A,pA,下同),两个wait操作,相当于进程A,先申请D资源,在申请E资源,相当于进程B,先申请E资源,在申请D资源,当pA,pB两个进程并发执行时,会出现一种极端情况,比如,操作系统,先调度A运行,会出现一种极端情况,
在这里插入图片描述
当执行完第一条语句,可能因为时间片等原因停下来了,这时,操作系统会调度B运行,如果出现这种调度情况,那么死锁产生了,当前这种状态相当于A进程占有了D资源,B进程占有了E资源,然后,都希望继续占有另外一部分,比如A还想占有E,这时E资源被B进程占用,申请不成功,被阻塞,同理,B进程申请D资源,被A进程占用,申请不成功,被阻塞,两个进程,这时候无法进行下去了,处于僵持状态,换句话说,两个进程永远都运行不完了,那当然不行了,我们通过这个例子总结一下:当死锁发生的时候,并发进程,都占用了一部分资源,同时还要申请对方所占用的另外一部分,也就是死锁发生的时候,通常都是占有了一部分资源,还需要别人占有的另外一部分,其中,一个很容易想到不产生死锁的办法,就是不允许部分分配,这就是AND型信号量的思想。
在这里插入图片描述
这样就可以确保不产生死锁现象,看起来,可以解决问题,但是这种方法有一个不好的地方,按照这个思想,如果有一个资源不满足,就不分配,那么可能会导致某些进程长期得不到运行,而且重要的是,资源利用很低,可行,但缺点比较严重,早期的学者也提出了很多改进。

第四种信号量类型,信号量集,为什么要提出这种类型的信号量?实际中,往往申请的资源数都是多个,前面提到的信号量的wait方法都是一个一个申请,比较麻烦,第二,执行多次wait容易产生死锁,所以提出第四种信号量,跟前面最大的区别就在于可以一次申请多个资源,有特殊的情况。

总结:
信号量解决资源的互斥访问,因为对临界资源同时访问,会照成不可再现的情况。所以,对临界资源,并发进程必须互斥访问,才能确保运行的可再现。利用信号量解决互斥访问。
我们只需要,
1.识别临界资源,设置互斥信号量,初值设为1
2.进入临界区之前,做一次wait(P)操作
3.退出临界区时,做一次signal(V)操作
就能确保并发进程对临界资源是互斥访问的

6.利用信号量机制解决同步问题

同步,就是直接制约关系。换一句话说,就是并发执行的进程之间存在着前驱后继关系。比如,我们前面举的例子。学习c语言,就是学习数据结构(C语言编写版)的前驱,学习这个事情要顺利进行,必须确保,学C语言先完成。数据结构的学习必须在学C语言之后(C语言学习就是数据结构(C语言编写版)学习的前驱,数据结构为后继,这个事情,顺序不能错,这就是直接制约,其实就是同步问题)。现在要解决的问题是,两件事情,也就是两个进程,一起进入内存,并发执行。如何确保前驱先执行,后继后执行呢?也就是这里讲的,如何实现同步。前驱图是用于描述进程之间的关系的,我们可以通过前驱图,来了解进程间的同步关系,前驱图中,一条边对应着一个同步关系。我们必须确保所有的进程,按同步关系来运行。在这里插入图片描述
如上图这个例子,有六个节点,代表有六个进程,进程之间的关系,看箭头,比如,我们这个图,s1是s2与s3的前驱,s2,s3必须在s1运行完了,才能运行,六个进程一起进入内存,并发执行,如何确保运行,按图中的关系运行,第一步,画出前驱图,第二步,途中一条边对应一个同步关系,为每一条边设置一个同步信号量,初值设为0。我们这个图有七条边,说明存在七个同步关系,所以需要定义,七个同步信号量,初值设置为0。第三步,实现同步关系。只需在前驱的后面加上signal语句,后继的前面加一条wait语句。
在这里插入图片描述
我们看这部分,s1是前驱,s2,s3是后继,则进程s1的代码如下:
在这里插入图片描述
看这段代码:
在这里插入图片描述
定义了七个信号量,初值为0,因为图中有七条边,有七个同步关系,接下来看这段:
在这里插入图片描述
看s1的代码,按套路,前驱的后面,对同步信号量做signal操作,因为,s1是s2,s3的前驱,所以在这里插入图片描述
s1语句的后面,做了两次signal,我们再看s2的代码,在这里插入图片描述
按套路,后继的前面对同步信号量做wait操作,所以,一开始就是wait(a),signal(c);signal(d),这个是因为,s2是后面两个进程的前驱依次完成一样的,就不多说了。现在我们看一下,如果按这个套路来写,有没有可能后继先运行,p1和p2一起进入内存,操作系统有可能先调度p2,在这里插入图片描述
这个是p2,p2一运行,就会执行第一条语句wait a,a的初值为0,实际上,一开始调度p2运行的话,就会被阻塞在wait操作这里,无法运行后继的语句,必须等,只有等前驱p1,前驱运行完后,对a做了signal操作,p2才能从wait返回,执行后继语句,从这里可以看出,只要按这个套路来写,就能确保:前驱先运行,后继后运行,从而也就实现了同步运行。

PS:由于本人水平有限,错误之处还请指正,不胜感激。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值