为什么要有进程同步
直接谈进程同步的概念有点抽象,我们先聊一聊为什么要有进程同步机制。我们的计算机系统刚开始是单道批处理系统,意思就是同一时间段内只能运行一个程序,这个程序运行完,才能运行另一个程序,这样就会导致运行效率太低,系统中的资源得不到充分的利用。怎么解决呢,就发明了多道批处理系统,多道程序并发执行,这样大大提高了系统资源的利用率。但是这种系统就会产生一些问题,比如有的资源,比如显示器,cpu,同一时间肯定只能一个程序使用,多个程序肯定不能同时使用显示器,这就是互斥关系,另外,有的两个进程间存在这样的制约关系:A程序的输出是B程序的输入,这是同步关系,为了解决这些问题,我们引入了进程同步机制。
进程同步的方法
怎么用进程同步的方法解决上述问题呢,其中一个方法就是信号量机制。
信号量机制
信号量机制是由荷兰的dijkstra发明的,有三种信号量机制,分别是
整型信号量,结构体型信号量和AND型信号量。
整型信号量
说白了就是用一个整数来进行管理,这个整数代表资源的数目,众所周知,对资源的操作有两种,一种是使用,一种是释放。他们分别对应两个函数:wait和signal
wait(S){
while(S<=0);
S--;
}
signal(S){
S++;
}
wait方法的意思就是首先看资源可不可以用,如果不可以用,也就是S<=0,它会一直等下去,直到S成为一个正数,然后S自减;signal的意思是使用完资源后释放资源,S自增。
结构体型信号量
为什么整型信号量用的好好的,要造一个结构体型信号量呢?因为整型信号量有个问题:我们会发现当S<=0的时候,会一直执行循环,也就是进程会处于忙等的状态。我们要解决忙等,也就是要让进程符合“让权等待”,就是要在进程无法使用资源的时候,释放处理机。那么问题就来了,该让哪个进程访问临界资源呢?我们可以使用一个链表来解决这个问题。所以我们就用一个结构体来描述资源,这个结构体长这样:
typedef struct{
int value;
struct process_control_block *list;
}semaphore;
这个结构体里边有两个变量,一个是value,用来记录资源的个数,下边这个是指针,指向下一个要使用临界资源的进程。
所以之前的使用和释放资源的函数就变成了这样:
wait(semaphore *S){
S->value--;
if(S->value<0) block(S->list);
}
signal(semaphore *S){
s->value++;
if(S->value<=0) wakeup(S->list);
}
使用信号量机制解决问题
上边咱们只是简单介绍了一下信号量机制,但别忘了咋们是要解决互斥关系和同步关系这两个问题的。
实现互斥关系
假设两个进程PA,PB具有互斥关系,也就是他们俩要使用一个临界资源,怎么做呢,我们设置一个mutex信号量,初值设为1,这样的话刚开始两个进程都能使用,他们使用的时候,先wait,wait完自然要让mutex-1,这样mutex为0,另一个就不能用了,使用完以后signal(释放),mutex重新变成1,另一个进程依然可以使用该临界资源。
semaphore mutex=1;
PA(){
while(1){
wait(mutex);
临界区
signal(mutex);
剩余区
}
}
PB(){
while(1){
wait(mutex);
临界区
signal(mutex);
剩余区
}
}
实现前驱关系
假设P1和P2有前驱关系,P1执行完,P2才能执行,那么怎么实现呢?可以设置一个公共的信号量S,初值设为0
进程P1中:S1;signal(S);
进程P2中:wait(S);S2;
这个是什么意思呢,就是先执行P1的语句,然后释放S,也就是S++,这样当P2执行wait的时候才可以执行,否则,不执行signal的话,S就为0,P2也无法执行,这样就实现了P1和P2的前驱关系。