进程描述与控制

2.4 进程同步

进程同步机制的主要任务,是对多个相关进程在执行次序上进行协调,使并发执行的诸进程之间能按照一定的规则共享系统资源,进行相互合作,使程序的执行具有可再现性。

两种形式的制约关系
(1)间接相互制约
在多个程序并发执行时,由于共享系统资源,使得这些程序之间形成相互制约关系。对于临界资源,要互斥的访问。为了保证程序有序的进行,由系统实施统一的分配。
(2)直接相互制约
多个进程为了完成同一任务而相互合作产生的直接制约关系。比如生产者消费者。

临界区:访问临界资源的一段代码称为临界区。
进入区:在对临界资源进行访问时,应先对临界区是否被访问进行检查,如被访问,设置标志。这段检查的代码称为进入区。
退出区:在访问结束后恢复为未被访问标志。

同步机制应该遵循的规则
(1)空闲让进
(2)忙则等待
(3)有限等待 应保证在有限时间内能够进入临界区,以免陷入“死等”状态
(4)让权等待 不能进入临界区时,要释放处理机,以免进入忙等

硬件同步机制

1.关中断
在进入锁测试之前关闭中断,直到完成锁测试并上锁之后再打开中断。这样进程在临界区执行期间不响应中断,即不会引发调度。保证了对锁测试和操作的完整性,有效的保证了互斥。
缺点是:
①滥用会导致严重后果
②时间过长影响系统效率,限制处理器交叉执行程序
③不适用于多CPU,因为在一个处理器上关中断,并不能防止进程在其他处理器执行相同的临界区代码
2.Test-Set 3.Swap
上述都能有效的实现进程互斥,当临界资源忙碌时,访问进程必须不断测试,不符合让权等待原则,也无法用于解决复杂的进程同步问题。

信号量同步机制

1.整型信号量
S表示资源数量,wait和signal表示两个原子操作,也称P、V操作
wait(S){
    while(S<=0);
    s--;
}
signal(S){
    S++;
}
因为要不断测试,也会出现忙等。
2.记录型信号量
记录型信号量不存在忙等现象。但是这样就会出现多个进程等待访问同一临界资源的情况。所以记录型信号量就增加了一个记录进程链表指针。
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的绝对值代表在该信号量表中已阻塞进程数目。
3.AND型信号量
AND型信号量用于多个进程共享多个临界资源时。其基本思想为,将进程在整个运行过程中需要的所有资源,一次性地分配给进程,用完一起释放。只要有一个不能进行分配,其余资源就一个也不分配。
4.信号量集
S(资源数)、T(资源分配下限)、D(一次申请的资源数目)

信号量的应用

1.利用信号量实现进程互斥
若要两个进程互斥的访问临界资源,只需设置一个互斥信号量mutex=1,然后将临界区置于wait和signal之间。
2.利用信号量实现前趋关系
比如我们希望先执行S1后执行S2,可以酱紫
S初始化为0
在进程P1中,用S1;signal(S);
在进程P2中,用wait(S);S2;

管程机制

对于信号量机制,每个要访问临界资源的进程必须自备同步操作wait(S)和signal(S)。使得大量的同步操作分散在各个进程中,不便于管理,还会因为同步操作使用不当导致系统死锁。
那么管程是指,代表共享资源的数据结构以及由对该共享数据结构实施操作的一组过程所组成的资源管理程序共同构成了一个操作系统的资源管理模块。每次只有一个进程进入管程。

经典进程的同步问题

读者-写者问题

利用记录型信号量解决读者-写者问题
首先要知道,读者在读的时候,允许其他读者来读。但是读者读时,不允许写着来写,同理,写者写时,不允许读者来读。
现在呢,为实现Reader与Writer进程间在读或写时的互斥而设置了一个互斥信号量Wmutex。另外 ,设置整型变量Readcount表示正在读的进程数目。由于只要有一个进程在读,便不允许Writer去写。因此,仅当Readcount=0,表示尚无Reader进程在读时,Reader进程才需要执行Wait(Wmutex)操作。若wait(Wmutex)操作成功,Reader进程便可去读,然后执行Readcount+1操作。同理,仅当Reader进程在执行了Readcount-1操作后其值为0时,才须执行signal(Wmutex)操作,以便让Writer进程写操作。又因为Readcount是一个被多个Reader进程访问的临界资源,因此,为它设置了一个互斥信号量rmutex。
semaphore rmutex=1,wmutex=1;
    int readcount=0;
    void read(){
        do{
          wait(rmutex);
          if(readcount==0) wait(wmutex); //这里之前一直不理解,很重要的一点,下面详细分析
          readcount++;
          signal(rmutex);
          ...
          perform read operation;
          ...
          wait(rmutex);
          readcount--;
          if(readcount==0) signal(wmutex);
          signal(rmutex);
        }while(TRUE);
      }
      void writer(){
        do{
          wait(wmutex);
          perform write operation;
          signal(wmutex);
        }while(TRUE);
      }
      void main() {
        cobegin
          reader(); writer();
        coend
      }
对于上面的程序,个人认为最难理解的就是对于读的处理。if(readcount==0) wait(wmutex);这个地方,我一直不理解,为什么要判断读者是否为零,然后再去wait(wmutex);其实就是用多线程的思想实现了可以多个读者进行读。

看下面这个程序
      wait(rmutex);                   |
      if(readcount==0) wait(wmutex);  |  
      readcount++;                    |
      signal(rmutex);                 |

这一部分相当于对于读者的一个门槛,也就是在计算readcount的时候,要进行互斥的操作。操作完了,就可以正式进入读阶段了,也就是perform read operation阶段。那么为什么要在互斥readcount中加入if(readcount==0) wait(wmutex);这句判断呢,其实就是一劳永逸的操作,当第一个读者进来的时候,还没有对readcount+1,所以readcount=0,把写操作锁住,说明有读者在读了,不能写了。那么之后再进来的读者,只要判断一下自己是不是第一个进来的,如果是,就加上锁,如果不是,就说明现在有读者在读,写锁已经加上了,自己不用再加了。

      ...
      perform read operation;
      ...
      wait(rmutex);
      readcount--;
      if(readcount==0) signal(wmutex);
      signal(rmutex);
那么后面这部分就好理解了,就是读完了,进行readcount-1,如果此时readcount=0,说明自己是最后一个读者,没有人在读了,那么就可以解开写锁,允许写操作的执行。

啊哈,终于搞懂读者写者问题了,也没有那么难嘛,嘿嘿!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值