1. 整型信号量
- 用一个整数型变量作为信号量,数值表示某种资源数
- 整形信号量与普通整型变量的区别:对信号量只能执行 初始化、P、V三种操作
- 整型信号量存在的问题:不满足让权等待原则
wait(S) { while(S <= 0); S = S - 1; } signal(S) { S = S + 1; }
2. 记录型信号量
- S.value 表示某种资源数,S.L指向等待该资源的队列
typedef struct { int value; struct process* L; }semaphore;
- P操作中,一定是先 S.value--,进程请求一个单位资源,之后可能需要执行block原语
- V操作中,一定是先 S.value++,进程是否一个单位资源,之后可能需要执行wakeup原语
- 注意:要能够自己推断在什么条件下需要执行block或wakeup
- 可以用记录型信号量实现系统资源的“申请”和“释放”
- 可以用记录型信号量实现进程互斥、进程同步
3. 实现进程互斥
- 分析问题,确定临界区
- 设置互斥信号量,初值为1
- 临界区之前对信号量执行P操作
- 临界区之后对信号量执行V操作
semaphore S = 1; // 初始化同步信号量,初始值为0
P1() { P(S); // 准备开始访问临界资源,加锁 进程P1的临界区; V(S); // 访问结束,解锁 }
P2() { P(S); // 准备开始访问临界资源,加锁 进程P1的临界区; V(S); // 访问结束,解锁 }
4. 实现进程同步
- 分析问题,找出哪里需要实现“一前一后”的同步关系
- 设置同步信号量,初始值为0(一般为0,可由用户设定)
- 在“前操作”之后执行V操作
- 在“后操作”之前执行P操作
semaphore S = 0; // 初始化同步信号量,初始值为0
P1() { 代码1; 代码2; V(S); 代码3; }
P2() { P(S); 代码4; 代码5; 代码6; }
理解:信号量S代表“某种资源”,刚开始是没有这种资源的。P2需要使用这种资源,而又只能由P1产生这种资源。
5. 实现进程的前驱关系
- 分析问题,画出前驱图,把每一对前驱关系对看成一个同步问题
- 为每一对前驱关系设置同步信号量,初始值为0
- 在每个“前操作”之后执行V操作
- 在每个“后操作”之前执行P操作

6. 补充
- P、V操作必须成对出现
- 缺少P(mutex)就不能保证临界资源的互斥访问
- 缺少V(mutex)会导致资源永不被释放,等待进程永不被唤醒