大家好,又见面了,我是你们的朋友全栈君。
本篇帖子继续上篇。有兴趣可以点击链接进行查看以前写过的文章。 【系统架构设计师】第一章:操作系统(1.2.2) 参考教材: 《系统架构设计师考试全程指导(第二版)》 《系统架构设计师教程》
1.2.2 信号量与pv操作
pv操作指的是两个:p操作和v操作。
有时候我们的进程在工作的时候,需要同时配合来干多件事情。比如,我们规定一个进程用来写入数据,另一个进程用来读取数据。
很显然,这连个进程是不能互相干扰的,因此我们就需要提前对系统进行一下告知:使用这些资源的权力我先占用了,其他人不要进来。
系统得知以后,如果有其他进程要写入数据或者读取数据,就会被系统安排成等待态。 等我们的进程使用资源完成以后,在告知系统,其他进程可以使用这些资源。
告知系统不要让其他进程操作这片内存的操作,就是p操作。 告知系统此进程已使用内存完毕,就是v操作。
我们因此可以得出使用pv操作的目的: 为了解决不同进程的互斥(都需要共享独占性资源时)和同步(完成异步的两个进程的协作)问题,需要使用pv操作。
想想看,如果是你,你会怎么用代码的方式解决呢?
我这里用的是c语言的方式。
首先,我们要有一个变量s。 在pv操作中被称为信号量,表现形式是一个整数s和一个队列。你可以姑且认为这是个整形变量。 当s>=0,代表某个资源的可用数; 当s<0,其绝对值代表阻塞队列中等待该资源的进程数。
其次是p操作。 根据我们开始所说,p操作就是告知系统这片资源已经被使用,因此只要在本来的资源数上减1即可。不过我们要考虑一种情况,就是当可用资源为0或负数的时候,那么就要让程序进入等待。 所以我们可以得出:
代码语言:javascript复制if((s=s-1)<0){
printf("执行p操作的进程进入等待");
}
最后是v操作。 这个同理,减一换成加一即可。
代码语言:javascript复制if((s=s 1)<=0){
printf("从阻塞队列中唤醒一个其他处于阻塞态的进程");
}
互斥实例
说了这么多,先来个实例,比如,对两个进程进行互斥控制。 我这里给了两个程序。 这两个程序是同时运行的,并且变量s是共享的,且初值为1。为了省事,这里我就简写了,省去了main之类的格式。
我对p和v进行了一些小的修改,这样更符合c语言的标准。
p:
代码语言:javascript复制if((s=s-1)<0){
printf("执行p操作的进程进入等待");
return 0;
}
else{
return 1;
}
v:
代码语言:javascript复制if((s=s 1)<=0){
printf("从阻塞队列中唤醒一个其他处于阻塞态的进程");
return 0;
}
else{
return 1;
}
进程A
代码语言:javascript复制while(1){
if(p(s)){
printf("操作一下");
}
v(s)
}
进程B
代码语言:javascript复制while(1){
if(p(s)){
printf("操作二下");
}
v(s)
}
仔细分析。 我们这里先说明一个前提,即使我们的程序同时运行,由于某些特殊的原因,是不可能真正的同时启动的,稍微会有一些误差,比如某个进程会晚0.00000000001秒。我们这里就假设A进程是比较快的那个。
第一次循环时,A先执行了p,然后s自减,输出了“操作一下”。
此可我们的B启动,也执行了p,注意此可A还没有执行V,也就是说s还没有自增。由于我们的s已经从1自减到了0,再次执行,s=-1,if不成立,什么都不输出。
当A执行完成v操作以后,我们的B已经到了第二轮,此可if成立,所以输出了“操作二下”。
以此类推,你会发现,A和B永远都时一个执行,另一个就跳过。这样,我们就使用pv操作对进程的互斥。
同步实例
同步也是类似的操作。直接上例程。不过这次比较特殊,需要用到两个信号量,s1=1和s2=1。 p:
代码语言:javascript复制if((s=s-1)<0){
printf("执行p操作的进程进入等待");
return 0;
}
else{
return 1;
}
v:
代码语言:javascript复制if((s=s 1)<=0){
printf("从阻塞队列中唤醒一个其他处于阻塞态的进程");
return 0;
}
else{
return 1;
}
进程A
代码语言:javascript复制while(1){
if(p(s1)){
printf("操作一下");
}
v(s2)
}
进程B
代码语言:javascript复制while(1){
if(p(s2)){
printf("操作二下");
}
v(s1)
}
仔细分析。
这个其实A和B无论是否同时运行都行。
假设我们的A先运行,执行第一遍的时候没什么问题,正常输出“操作一下”。
但是当执行第二遍的时候,你会发现,A不会输出了,因为我们的s1从开始循环第二遍的0变成了-1,所以此刻if时不成立的。
而当我们运行B的时候,B执行v(s2),最终将p增到1,A才会继续走。
这样,我们就完成了进程之间的同步。
本篇内容主要介绍的是pv操作的一些细节。 下一节已更新。 【系统架构设计师】第一章:操作系统(1.2.3)死锁问题
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/159780.html原文链接:https://javaforall.cn