操作系统——进程的同步与互斥

临界资源:
在某段时间内只允许一个进程使用的资源。进程之间互斥访问。实现对临界资源的共享。
eg:(硬件资源)打印机、磁带机,(软件资源)变量、文件。

临界:
每一个进程访问临界资源的那段代码。进程互斥的进入自己的临界区,就可保证互斥访问。
同步机制的四条准则:

  • 空闲让进:(空闲时进程可以进)
  • 忙则等待:(忙碌的时候进程等待)
  • 有限等待:(对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,以免陷入“死等”)
  • 让权等待:(进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入“忙等”)

同步机制

  • 信号量机制

    • 整形信号量——表示资源数量的整型量s。通过申请资源wait(s),释放资源signal(s)来实现互斥访问。wait、signal也称P、V操作

      但是整形信号量无法实现让权等待。一个进程因为资源不够进入阻塞,其它进程也无法进入

    • 记录型信号量——在整形信号量的基础上加入一个阻塞队列。若资源不够则进入阻塞队列并释放资源,这样就实现了让权等待

      typedef struct{
          int value;//资源数目
          struct process_control_block *list;//阻塞链表
      }semaphore;
      
      wait(semaphore s){
          s.value--;
          if(s.value<0){//资源不够就放入阻塞列表
              block(s.list);
          }
      }
      /*当s.value<0之后,value的绝对值是阻塞队列中的进程个数*/
      
      signal(semaphore s){
          s.value++;//阻塞队列中进程减少
          if(s.value<=0){//阻塞队列中还有进程
              wakeup(s.list);//这就让一些进程继续执行
          }
      }
      
    • AND型信号量——当不止缺少一个资源时,缺少哪一个资源就把他放入哪一个资源的阻塞队列中

      Swait(semaphore s1,s2,...,sn){
          while(TRUE){
              if(s1.value>=1 &&...&& sn.value>=1){
                  for(i=1;i<=n;i++){
                      si.value--;
                  }
                  break;
              }
              else{
                  /*找到第一个si<1时,重置程序计数器,把这个进程放在si的阻塞队列中*/
              }
          }
      }
      
      signal(semaphore s){
          while(TRUE){
              for(i=1;i<=n;i++){
                  si.value++;
                  /*把进程放入就绪队列*/
              }
          }
      }
      
    • 信号量集——当进程对某一资源的需求不止一个时,用tn表示进程还需资源数,dn表示进程共需资源数

      semaphore s1,s2,...,sn;
      Swait(s1,t1,d1,...,sn,tn,dn){
          while(TRUE){
              if(s1.value>=t1 &&...&& sn.value>=tn){//条件函数不一样
                  for(i=1;i<=n;i++){
                      si.value-=di;
                  }
                  break;
              }
              else{
                  /*找到第一个si<t1时,重置程序计数器,把这个进程放在si的阻塞队列中*/
              }
          }
      }
      
      
      signal(semaphore s1,s2,..,sn){
          while(TRUE){
              for(i=1;i<=n;i++){
                  si.value+=di;
                  /*把进程放入就绪队列*/
              }
          }
      }
      
  • 管程机制
    管程:代表共享资源的数据结构+实施操作的一组过程。
    引入管程的原因:信号量机制是一种方便有效的进程同步工具。但每次访问临界资源时都需要进程自身进行wait(S)和signal(S)操作。这样,大量的同步操作分散在不同进程中,会给系统管理带来负担,另一方面同步操作使用不当容易导致死锁。
    管程封装了同步操作,对进程隐蔽了同步细节,简化了同步功能的调用,避免了有意或无意的违法同步操作,给编程带来便利。
    管程的特点:互斥性(一个管程中只能有一个活跃的进程)、共享性(管程可以互斥地被多个进程访问)、安全性封装性
    当进程缺少哪一个条件,就将该进程放入哪一个条件的阻塞队列中,同时释放管程。
    进程的同步.管程结构

  • 硬件同步机制

进程同步与互斥问题

  • 生产者与消费者

    在生产者与消费者直接设置有n个缓冲区的缓冲池,——相当于一个循环队列。生产的数据放入缓冲池,消费者从缓冲池中拿数据。
    信号量的设计:这里就用了互斥信号量和整型信号量。互斥信号量实现生产者与消费者的互斥访问。整型信号量为了保证资源足够才争取锁。

    /*全局变量*/
    typedef struct{//循环缓冲表
        ...
    } item; 
    item buffer[n];
    int in=out=0;//in指针,out指针 in=(in+1)%n   out=(out+1)%n
    item nextp,nextc;//每次要生产、消费的产品
    semphore mutex=1//互斥信号量,初始为1
    semphore empty=n,full=0  //资源信号量,整型信号量
    /*主程序*/
    void main(){
        cobegin
            producer1;...producerm;
        	consumer1;...consumern;
        coend
    }
    /*生产者*/
    produceri:
    {
        repeat
            produce an item in nextp;
        	wait(empty);   //空缓冲区减少,wait顺序不能互换,确保有资源才争取锁
        	wait(mutex);
    	//Swait(empty,mutex);  //使用and信号量。多个资源一同申请,防止死锁。不用考虑先后顺序
        	buffer[in]=nextp;
        	in=(in+1)%n;
        	signal(mutex);
        	signal(full); //生产过后要去通知消费者,满缓冲区增加
    	//Ssignal(mutex,full);   //and信号量释放
        until false;
    }
    /*消费者*/
    consumerj:
    {
        repeat
        	wait(full); //满缓冲区减少
        	wait(mutex);
    	//Swait(full,mutex);  //使用and信号量。
        	nextc=buffer[out];
        	out=(out+1)%n;
        	signal(mutex);
        	signal(empty); //通知生产者空缓冲区增加
    	//Ssignal(mutex,empty);   //and信号量释放
        	consumer the item in nextc;
        until false;
    }
    
  • 读者与写者
    读进程可以同时读,但是写进程之间、写进程和读进程要保持互斥。
    信号量的设计:

    1. 为了实现写互斥,则需要写一个写互斥信号量wmutex = 1
    2. 读进程之间不互斥,则用一个计数器记录读进程个数readcountreadcount为临界资源,需要对其设置互斥信号量rmutex = 1
    3. 读写之前都需要申请wmutex,当readcount=0时读进程才需要申请wmutex,避免第二次申请失败
    int readcount=0;
    semaphore wmutex=1,rmutex=1;
    main(){
        cobegin
          read1,read2,.....
          write1,write2....
        coend
    }
    write{
        repeat
            wait(wmutex);
            perform write operation;
            signal(wmutex);
        until false;
    }
    read{
        repeat
            wait(rmutex);  // 访问readcount之前需要申请互斥信号量
            if(readcount==0)  then wait(wmutex);//如果没有读进程,就申请写信号量,避免写进程执行。加一个条件避免重复申请。
            readcount++;
            signal(rmutex);//可以读了,就释放,便于其它读进程读。
            perform read operation;
            wait(rmutex);
            readcount--;
            if(readcount==0)  then signal(wmutex);//通知写进程,可以写了
            signal(rmutex);
        until false;
    }
    
  • 哲学家进餐
    五个哲学家,在一张圆桌上,桌上五支筷子,分别位于哲学家的左右两边。哲学家只有同时拿到两支筷子才能吃。
    因为拿到两支才能吃,所以左右两支筷子都设一个信号量。

    semaphore chopshick[0,...,4]={1,1,1,1,1};//筷子信号量数组
    main(){
        cobegin
           	philosopy0;
     	    philosopy1;
        	philosopy2;
        	philosopy3;
        	philosopy4;
        coend
    }
    
    philosopyi:
    {
        repeat
            think;
        	wait(chopstick[i]);  //申请左边筷子
        	wait(chopstick[(i+1)mod5]);  //右边
        	eat;
        	signal(chopstick[i]);
        	signal(chopstick[(i+1)mod5]);
        until false;
    }
    

    但是当所有人同时申请左右两边的筷子时会陷入死锁
    解决办法:当哲学家左右两只筷子都能使用时,才允许拿起筷子。或者最多四人同时拿筷子。或者使用AND信号量

    philosopyi:
    {
       repeat        
    		think;
       		Swait(chopstick[i],chopstick[(i+1)mod5]);
       		eat;
       		Ssignal(chopstick[i],chopstick[(i+1)mod5]);
       until false;
    }
    
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值