目录
临界资源
临界区
信号量机制
整形信号量
记录型信号量
AND信号量
信号量集
信号量的应用
实现进程互斥
实现前驱关系
管程机制
总结
临界资源
I/O设备属于临界资源。著名的生产者-消费者问题就是关于临界资源的争夺产生的进程同步的问题。
生产者-消费者
描述:一群生产者生产产品,产品生成完以后运到仓库储存,仓库的大小有限,这里的仓库在表示中抽象成了缓冲区。一群消费者需要到仓库中取产品进行消费。
代码:
#include "stdio.h"
#include "unistd.h"
#define MAXSIZE 100
int in = 0;
int out = 0;
int count[1] = {0};
int child_process_id = 0;
char buffer[MAXSIZE];
void producer();
void comsumer();
void main() {
__pid_t pid = fork();
//int n = 0;
//count = &n;
printf("The process id = %d\n", pid);
if(pid == -1) {
perror("fork error!");
} else if(pid == 0){
printf("Hello from child process: %d\n", getpid());
child_process_id = getpid();
comsumer();
} else {
printf("Hello from father process: %d\n", getpid());
//__pid_t pid1 = fork();
producer();
}
}
void producer() {
while (1)
{
while (*count == MAXSIZE){
printf("producer:%d, %d\n", *count, getpid());
scanf("%d", count);
}
in = (in+1)%MAXSIZE;
(*count)++;
}
}
void comsumer() {
while (1)
{
//FILE *file = fopen("comsumer.txt", "w");
//fputs(buffer, file);
//fclose(file);
while (*count == 0){
printf("comsumer:%d, %d\n", *count, getpid());
scanf("%d", count);
}
//getchar();
out = (out+1)%MAXSIZE;
(*count)--;
}
printf("Comsumer exit!\n");
}
输出结果:
临界区
count可以理解为临界资源。
临界区:访问邻接资源的代码成为临界区。
访问count代码行成为临界区。
while (*count == MAXSIZE){//进入消费者临界区的条件
(*count)++;//生产者临界区
while (*count == 0){//进入消费者的临界区的条件
(*count)--;
信号量机制
整形信号量
代码:
wait(S){
while(S <= 0) ;
S--;
}
signal(S) {
S++;
}
定义:
整形信号量S | 简称 |
wait | P |
signal | V |
记录型信号量
代码:
typedef struct{
int value;
struct process_control_block *list;
}semaphore;
wait(semaphore *S) {
S->value--;
if(S->value < 0)block(S->list);
}
signal(semaphore *S) {
S->value++;
if(S->value<=0) wakeup(S->list);
}
S->value | 某类资源的数目 |
S->list | 等待使用某类资源的进程队列 |
block | 阻塞进程队列,后续进程无法使用该类资源 |
wakeup | 唤醒进程队列,可以使用该类资源 |
AND信号量
前面的信号量都是解决单个信号时出现的问题,如果出现两个或者以上的信号量呢?
信号量Dmutex = 1
信号量Emutex = 1
进程A:
进程B
由于并发进程存在随机性;所以存在以下的执行顺序:
A.P(Dmutex) | 0 | |
B.P(Emutex) | 0 | |
A.P(Emutex) | -1 | A阻塞 |
B.P(Dmutex) | -1 | B阻塞 |
伪码:
Swait(S1,S2,...,Sn)
{
while(true){
if(Si >=1 && ... Sn >=1) {
for(i = 1; i <=n; i++)Si--;
break;
} else {
将所有进程放入等待队列中
}
}
Ssignal(S1,S2,...,Sn)
{
while(true){
for(i = 1; i <=n; i++)Si--;
}
将所有进程放入就绪队列中
}
信号量集
如果一次性对多个进程的信号量做操作,同时每一次不是-1,而是减一个不确定值。信号量集可以解决这个问题。
Swait(S1,t1,d1,...,Sn,tn,dn);
Ssignal(S1,d1,...,Sn,dn);
信号量的应用
实现进程互斥
mutex的取值范围为-1,0,1时,结合以下的代码可以实现进程互斥。
while(1) {
wait(mutex);
临界区;
signal(mutex);
}
实现前驱关系
如果p1,p2进程存在p1->p2的关系,可以通过信号量机制,来实现前驱关系。
假设p1,p2都需要mutex = 0的信号量。
p1:signal(mutex);
p2:wait(mutex);
如果用有向图的理论来解释:
signal相当于p1到p2的边的起点,而wait相当于这条边的终点。
管程机制
管程是前面讲到的集中信号量机制的一个封装,统一的使用管程方法来实现进程的互斥,同步问题。
总结
本文从进程同步的一些基本元素:临界资源,临界区,信号量和管程机制,并且结合代码和伪码。来阐述一些概念。