- 实现进程互斥
-
分析问题,确定临界区
-
设置互斥信号量,初始值为1
1代表系统中某种资源的数量,而临界区在同一个时间段只允许一个进程 对他进行访问, 因此我们可以将它理解为一个特殊的资源,而这个资源 只有一个。只有当他被释放了才可以被另一个进程使用
-
在临界区之前对信号量执行P操作
-
在临界区之后对信号量执行V操作
semaphore mutex = 1; //信号量初始化
P1(){
P(mutex); //加锁
临界区代码
V(mutex); //解锁
}
P2(){
P(mutex);
临界区代码
V(mutex);
}
PV原语通过操作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序。
信号量的概念1965年由著名的荷兰计算机科学家Dijkstra提出,其基本思路是用一种新的变量类型(semaphore)来记录当前可用资源的数量。有两种实现方式:1)semaphore的取值必须大于或等于0。0表示当前已没有空闲资源,而正数表示当前空闲资源的数量;2)
semaphore的取值可正可负,负数的绝对值表示正在等待进入临界区的进程个数。
信号量是由操作系统来维护的,用户进程只能通过初始化和两个标准原语(P、V原语)来访问。初始化可指定一个非负整数,即空闲资源总数。
P原语:P是荷兰语Proberen(测试)的首字母。为阻塞原语,负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。操作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;
V原语:V是荷兰语Verhogen(增加)的首字母。为唤醒原语,负责把一个被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息。操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒之。
根据以上代码可知,P1进程先执行P操作信号量由1变为0,如果发生进程切换到P2又执行P操作,此时信号量为-1,P2会执行Block操作,将自己变成阻塞态。只有当P1执行完释放系统资源之后才会唤醒P2让他顺利的执行下去。
对于不同的临界资源我们需要设置不同的信号量,并且PV操作必须成对出现。没有V升降摄像头永不落下🤣🤣🤣🤣。
- 实现进程同步
进程同步:要让各并发进程按要求有序地推进。
用以下代码为例,P1、P2并发执行,由于存在异步性,因此P1、P2交替推进,次序不确定。但是假如代码4的前提是要代码1、2、3运行完事,那么这就是进程同步的问题了,让本来异步并发的进程互相配合,按照我们的期望有序推进。实现“同步关系”,即为保证“一前一后”执行的两个操作。
P1(){
代码1;
代码2;
代码3;
}
P2(){
代码4;
代码5;
代码6;
}
设置同步量S,初始值为0
在“前操作”之后执行V(s)
在“后操作”之前执行P(s)
semaphore S=0;//初始化信号量
P1(){ P2(){
代码1; P(S);
代码2; 代码4;
V(S); 代码5;
代码3; 代码6;
} }
若先执行到V(S)操作,则S++后S=1。之后当执行到P(S)操作时,由于S=1,表示有可用资源,会执行S–,S的值变回0, P2进程不会执行block原语,而是继续往下执行代码4。
若先执行到P(S)操作,由于S=0,S–后S=-1,表示此时没有可用资源,因此P操作中会执行 block 原语,主动请求阻塞。之后当执行完代码2,继而执行V(S)操作,S++,使S变回0由于此时有进程在该信号量对应的阻塞队列中,因此会在V操作中执行wakeup原语,唤醒P2进程。这样P2就可以继续执行代码4了。
3. 实现进程的前驱关系
进程P1中有句代码S1,P2中有句代码S2…P3…P6中有句代码S6。这些代码要求按如以下前驱图所示的顺序来执行:
每一对前驱关系都是一个进程同步问题,需要保证一前一后的操作,按照以下步骤即可:
·要为每一对前驱关系各设置一个同步变量
·在“前操作”之后对应的同步变量执行V(s)
·在“后操作”之前对应的同步变量执行P(s)
例如S1为S2、S3的前操作所以在S1结束后进行V(a)、V(b)(注:V(a)、V(b)代表S2、S3)操作,而S2、S3为后操作因此在S2、S3前进行P操作依次类推.