什么是原语?
答:执行时不可中断的过程。
P操作P(s):将整形型号量s的值减去1,如果结果小于0,那么调用P(s)的进程,将进程的信号量s的等待状态。
V操作V(s):将整形信号量s的值减去1,如果结果大于0,那么释放一个等待信号量s的进程。
/* 关于P操作 */ void P(semaphore *s){ s->value = s->value - 1; if(s->value < 0){ insert(CALLER, s->PCB); /* 将调用进程插入到等待信号量s的进程队列中 */ block(CALLER); /* 堵塞调用进程 */ } }
/* 关于V操作 */ void V(semaphore *s){ PID proc_id; /* 执行的进行号 */ s->value = s->value + 1 if(s->value <= 0){ remove(s->PCB, &proc_id); /* 从等待信号量s的进程中摘除proc_id该进程 */ wakeup(proc_id); /* 唤醒该进程 */ } }
值得注意的是:pv执行时,唤醒的顺序是由系统决定,唤醒的顺序不会影响程序正确性。
PV操作实现进程同步的操作
执行步骤:
1.为各进程设置私有信号量;2.为私有信号量赋初值;3.根据pv操作以及私有信号量设置各进程的执行顺序。
那问题来了:什么是私有信号量?
答:私有信号量:各进程发送的用于控制进程同步的消息。公有信号量:用于进程的互斥信号量。
二者的区别:私有信号量只与制约和被制约的进程有关,与整组并发进程无关。
经典案列:现有m个生产者和n个消费者,它们共享可存放k件物品的缓冲区。
int buffer[k]; /* 设置缓冲区以及容量 */ semaphore s1, s2, s; /* 设置信号量:s1(当进程缓冲区满时,生产者不能往满的缓冲区执行放操作的信号量),s2(当进程的缓冲区空时,消费者不能往空的缓冲区执行取操作的信号量), s(生产者与消费者互斥的信号量) */ int in, out; /* 设置存储的操作(说白了就是存放的位置) */ /* 赋初值 */ s.value = 1; s1.value = k; s2.value = 0; Cobegin repeat produceri; /* 生产者的执行 */ repeat consumerj; /* 消费者的执行 */ Coend; /* 生产者进程 */ process produceri{ int item; 生产者存放一件产品到item中; P(&s1); /* 执行P操作,检查是否能够进行放操作 */ P(&s); /* 执行P操作,检查临界区是能能够进入 */ buffer[in] = item; /* 将产品放到buffer缓冲区中 */ in = (in + 1) % k; /* 往后移动一位 */ V(&s2); /* 执行V操作 */ V(&s); /* 执行V操作 */ } /* 消费者进程 */ process consumerj{ int item; P(&s2); /* 执行P操作,检查是否能够进行取操作 */ P(&s); /* 执行P操作,检查临界区是能能够进入 */ item = buffer[out]; /* 取一件产品 */ out = (out + 1) % k; V(&s1); V(&s); 消费产品; }