定义
是用于进程之间传递信息的一个整数值,于1965年由 Dijkstra 提出。定义如下:
struct semaphore {
int count; // 需要传递的整数值
queueType q; // 进程队列
}
这是一个非常难以理解的概念。
使用
只能实施三种操作。即 初始化,P操作,V操作。P、V操作都是原子操作。可以认为,P操作即消耗资源,V操作即释放资源,两者必须成对使用。在P操作和V操作之间是临界区。
-
P操作
P操作使信号量的值减1,假设某个进程 A 使用了P操作,消耗一个资源。当资源数小于0时,由于无资源可用,进程阻塞等待,让出CPU资源。
P(struct semaphore& s) {
s.count--;
if (s.count < 0) {
// 1. 设置 A 为阻塞状态
// 2. 将该进程插入相应的等待队列 s.q 的末尾
// yield()让出CPU重新调度
}
}
-
V操作
V操作使信号量的值加1,假设某个进程 B 使用了V操作,释放了一个资源,则如果信号量 s.count < 0,则说明还有进程在等待资源。此时可以唤醒一个等待状态的进程。
V(struct semaphore& s) {
s.count++;
if (s.count <= 0) {
// 1. 唤醒等待队列 s.q 中等待的一个进程
// 2. 改变其状态为就绪态,并将其插入就绪队列
}
}
常见误解
-
如果说 s.count < 0 代表无可用资源,那为什么 V 操作还会唤醒等待队列 s.q 中的进程,可以执行吗?
s.q 中的进程是经历了 P 操作的进程,也就是说当被唤醒后,无需再判断 s.count < 0,而是直接往下执行。因此这样做是没有问题的。
想象一个情况,有4个进程 A, B, C, D 请求仅有1个的资源,都进行 P 操作,最后 s.count = -3,假设A拿到资源。A执行完后进行V操作释放资源,s.count = -2,此时显然资源是有的,唤醒阻塞队列中的进程即可继续执行。
因此,说 s.count < 0 代表无可用资源并不准确,它只是代表一个供小于求的状态。
参考
- 某博客
- 操作系统课程视频
网友评论