【操作系统基础】临界区问题 和 和原子操作的理解 和 互斥锁的实现和理解

临界区问题

在这里插入图片描述


每个并发的进程都有一个代码段,被叫做临界区,这个代码段也是临界区可能会改变并发进程的公告资源数据,更新数据,写入文件等操作;
在并发的进程中,当有一个进程正在执行临界区的代码时候,其他并发的进程是不可以被允许去执行临界区的代码的,换句话说:两个进程是不可以不可能同时执行临界区的内容;

总的来说:临界区问题就是设计一种协议:这种协议它可以保证进程之间相互协作;


进程进入临界区协议

在这里插入图片描述


当我们一个进程需要进入临界区时候,需要获取进入临界区的许可,当我们离开临界区时候,需要归还临界区的许可;


临界区的管理准则

在这里插入图片描述

  1. 首先的遵循-----互斥:互斥的意思:当多个并发进程都需要访问临界区的资源时候,只能有一个进程可以进去临界区访问,其他进程不可以访问,其他进程需要等到进入临界区的进程访问结束后,才有资格访问临界区,这就是互斥;

  2. 前进:前进意思是:当临界区有进程访问资源时候,其他进程必须等待,临界区没有进程访问时候,就必须选一个进程去访问临界区资源,这就是前进;

  3. 有限的等待:这个意思就是:当有一个进程在临界区访问资源时候,其他等待进程不可以无线的等待,也就是说,等待进入临界区的进程必须在某个时候也要进入临界区资源;


用我们中国人的话来讲:说临界区的管理准则是
在这里插入图片描述
有空让进:临界区没人访问就进去,也就是前进的意思;
择一而入:临界区只能选取一个进程进去访问,也是互斥的意思;
无空等待:临界区有进程在访问时候,其他进程必须等待,也就是互斥意思;
有限等待:等待进入临界区的进程,不可以无线等待;
让权等待:在临界区访问资源的进程,不能一致待在临界区;


喂金鱼案例理解临界区问题

首先我们假设一个场景:
有两个人一个Alice 和 Tom:这两个人养了一条鱼,为了使得这条鱼生存下来,它们制定了金鱼生存法则;
在这里插入图片描述


其次我们抽象出场景为代码:
在这里插入图片描述
第一天:
第一种方案:
先是Alice观察鱼没喂,所以Alice喂金鱼;然后到Tom观察金鱼,发现金鱼喂了,所以不喂金鱼,此时金鱼是生存下来;等价于Alice进程先执行结束,Tom进程再去执行;


第一种方案:
先是Tom观察鱼没喂,所以Tom喂金鱼;然后到Alice观察金鱼,发现金鱼喂了,所以不喂金鱼,此时金鱼是生存下来;等价于Tom进程先执行结束,Alice进程再去执行;


第三种方案:
先是Alice观察没喂鱼,Ailce刚打算喂金鱼,然后立马到Tom观察金鱼也没被喂,所以Tom也喂金鱼,当Tom喂完后,又到Alice它也继续喂金鱼;此时金鱼被喂了两次,直接撑死;等价:两个并发进程Ailce和Tom交替执行代码,喂鱼的动作被访问了两次;


基于第三种方案:
我们观察临界区资源是 喂鱼这个动作,和观察鱼是否被喂养这个动作
抽象出来就是两个并发进程都需要写入和读这个金鱼的资源;

由于第三种方案,并没有满足互斥的原理,也就是两个并发进程都一起访问到了临界区资源,比如上面两个进程都观察鱼是否被喂养,都喂鱼这个动作;这就会导致程序出错了;
在这里插入图片描述


第二天:她两又买了一条金鱼:修改了原来的方案:发现没人留下纸条,那么久留纸条,然后再观察鱼是否被喂,没又被喂就喂鱼,喂完鱼久撕开纸条;他们以为这也鱼就不会撑死;
在这里插入图片描述


首先,当两个进程都是先执行完自己,再执行它人的,这也就不会有问题;
但是当有交替执行,出现了这总情况:
Alice判断没纸条,刚打算留纸条,然后Tom开始执行代码,也判断没留纸条,那么tom就留下纸条,此时。。。后面的事情可想而知,他们都会同时进入观察金鱼没被喂的情况,又会导致金鱼撑死;
这个原因也是违法了互斥原来,他们这种留纸条的方案,并没有解决进入临界区资源是只有一个进程可以进去的问题,都是一起进去了;


第三天,他们两又买了一条金鱼,又改变了策略,先留纸条,再判断对方有没有留纸条;

在这里插入图片描述
此时:假如两个进程是并发交替执行,也会在导致鱼被饿死;
这个是违反了前进的规则:临界区的资源必须有一个进程需要进去访问;


我们发现上面的方式:都会使得鱼死掉;
撑死和饿死,假如金鱼都是死,哪种死法比较好呢?
可能是饿死比较好,因为撑死就是程序执行出错的结果,而饿死确实使得程序不继续执行下去而已;


第四天,他们搞了一种新的方式:这种方式无论怎么样都可以使得鱼活下来;
在这里插入图片描述


但是这种设计方式:使得这两个进程的代码不一样了。这样的设计是好的嘛?
这样会使得管理变得复杂,维护性差!

在这里插入图片描述


互斥锁

在这里插入图片描述
操作系统设计人员给我们提供一些软件工具,帮我们解决临界区带来的问题,这个最简单的软件工具就是互斥锁;
一个进程必须获得一把锁才可以进入临界区;
一个进程在退出临界区时候必须释放该锁;


那么对于上面喂金鱼的案例:我们就有一种办法,使用互斥锁去解决;互斥锁解决会使得两份代码都是一样的,并不会两边都不一样
在这里插入图片描述


上锁:
包括两个动作
第一个动作:等待锁被打开,因为上锁之前,需要测试判断,该临界区是否已经有人进去了,假如有人进去,那么表示上锁了,那么等待该所被打开,你才可以进去,假如没人进去你就可直接获得锁了;
第二个动作:获得锁且上锁;


原子操作

在这里插入图片描述


原子操作:test_and_set()的实现

在这里插入图片描述


它主要是干的事:
给传入的参数是true,就return true;同时给传入参数设置为false;
给传入的参数是false,就return false;同时给传入参数设置为false;


注意这个test_and_set是原子操作,是不可能被打断的,也就是这个函数是一气呵成执行到结束的,不会出现走走停停的操作,这是由操作系统保证的;


lock()锁的实现

lock锁的实现,需要借助test_and_set()这个原子操作的函数来帮助实现:
在这里插入图片描述


分析这段代码:首先我们假设available是true:表示还没获得锁,那么我们lock就不需要等待,也就是while循环不需要执行,直接获得锁,上锁即可进入临界区;

当一个进程要进入临界区时候,那么开始是没上锁,所以available是true;分析这个while循环时如何执行的:
在这里插入图片描述


很明显,在没有锁的前提上,lock是while循环是不执行,那么你就可以直接上锁了,使得available为false;
表示上锁状态,当前进程在访问临界区资源,其他进程不可以访问,除非当前进程unlock,其他进程才可以访问;


当一个进程在访问临界资源时候,如果另一个进程也要访问临界区资源时候,那么它就会执行lock执行,在lock里面就会执行test_and_set,由于前面有一个进程在访问临界资源了,所以此时当前进程进入lock时候,available就为 false, 那么就会执行while,一直测试test_and_set等待锁变为true;只有当访问完临界资源的那个进程,调用了unlock时候,才会释放锁,使得available = true,此时当前进程测试到了 available为true,就会退出循环,开始进入临界区资源了。


忙式等待

在这里插入图片描述


猜你喜欢

转载自blog.csdn.net/m0_46606290/article/details/124103966