操作系统中的锁——信号量(同步信号量、互斥信号量)、P/V操作、自旋锁

1、进程间的通信(InterProcess Communication,IPC)

进程之间的通信常分为两种:低级通信、高级通信

  • 低级通信:通过信号量实现进程之间的互斥和同步关系。
  • 高级通信:管道、共享内存、消息队列等。
  1.1 进程间的互斥
  • 共享资源:①慢速的硬设备,如打印机;②软件资源,如共享变量、共享文件和各种队列等。
  • 临界资源:就是一次仅允许一个进程使用的系统中共享资源。
  • 临界区(critical section):就是并发进程访问临界资源的那段必须互斥执行的程序。

  避免竞争条件只需要阻止多个进程同时读写共享的数据就可以了,也就是保证同时只有一个进程处于临界区内。

  我们可以用《现代操作系统》一书的配图来理解。在这里插入图片描述

  1.2 进程间的同步

  同步的原因:一组进程要合作完成一项任务。
例如:
  两个用户进程共享缓冲区。计算进程将计算结果送入共享缓冲区,打印进程从缓冲区取数据打印。缓冲区空时不取数据,满时不送数据。
  由于计算进程与打印进程访问缓冲区的速度不匹配,需要进行同步处理。为了使进程同步,需要引入信号量机制。

  1965年,荷兰学者Dijkstra提出的一种同步机制。
  基本原理:两个或多个进程通过简单的信号进行合作,一个进程被迫在某一位置停止,直到它接收到一个特定的信号。为了发信号,需要使用一个称作信号量的特殊变量。

typedef struct{ 	//信号量的类型描述
      int value; //表示该类资源的可用数量
      struct process *list; //等待使用该类资源的进程排成队列的队列头指针。
   }semaphore, sem;

对信号量S的操作只允许执行P、V原语操作

P操作原语: //wait(s) ;  
void P (sem &s)
{  s.value = s.value-1;   //表示申请一个资源(或通过信号量s接收消息)
    if (s.value < 0)
         { add this process to s.list;
            block( );
         } //资源用完,调用阻塞原语。“让权等待”
}
V操作原语:// signal(s);  
Void V (sem &s) {  
     s.value = s.value+1;
      //释放一个资源(或通过信号量s发消息) 
  if (s.value <= 0) { 
      remove a process P from s.list;  
      wakeup( );
     }//表示在信号链表中,仍有等待该资源的进程被阻塞。 调用唤醒原语。
}

操作系统正是利用信号量的状态来对进程和资源进行管理的

  1.3 利用信号量实现进程之间的互斥

  设置一个互斥信号量mutex,初值为1,表示该临界资源空闲。
  调用P(mutex)申请临界资源——mutex变为0。
  调用V(mutex)释放临界资源——mutex变为1。
  只需把临界区代码置于P(mutex)和 V(mutex)之间,就可实现临界资源的互斥使用了。

  1.4 生产者、消费者问题

生产者和消费者是相互合作进程关系的一种抽象
生产者:当进程释放一个资源时,可把它看成是该资源的生产者,
消费者:当进程申请使用一个资源时,可把它看成该资源的消费者。

举个简单的例子:
  假定有一组生产者和消费者进程,通过一个有界环形缓冲区(有k个缓冲区)发生联系。生产者向缓冲区放产品,消费者从中取产品。
当缓冲区满时,生产者要等消费者取走产品后才能向缓冲区放下一个产品;当缓冲区空时,消费者要等生产者放一个产品入缓冲区后才能从缓冲区取一个产品。

这个环形缓冲区是一个临界资源。互斥使用

//empty:表示空缓冲区的个数,初值为k
//full:有数据的缓冲区个数,初值为0
//mutex:互斥访问临界区的信号量,初值为1

int  mutex=1, empty=k, full=0,  i=0, j=0;    
DataType  array[k];    

Producer:
        … 
        produce a product x;
        P(empty);    	//申请一个空缓冲
        P(mutex); 	//申请进入缓冲区
        array[i] = x;   	//放入产品
        i = (i+1)mod k;
        V(full);  	// 有数据的缓冲区个数加1
        V(mutex); 	//退出缓冲区
…


 Consumer:
          …
          P(full); 	//申请一个产品
          P(mutex); 	//申请进入缓冲区
          y = array[j]; 	//取产品
          j = (j+1)mod k;
	     V(empty); 	//释放1个空缓冲
         V(mutex); 	//退出缓冲区


2、死锁

  在计算机系统中有很多独占性的资源,在任一时刻只能被一个进程使用。如打印机、磁带机。

例如:一个计算机系统,有4台磁带机和2个并发执行的进程。某一时刻,每一进程都已占有2台磁带机,还要再请求一台才能完成它们的任务。这时,由于再无空闲的磁带机,两个进程就处于永远的等待状态,系统产生了死锁。

  我们上边讲到的P、V操作,如果使用不当,就会造成死锁的情况。若将生产者进程中的两次P操作交换顺序,

//empty:表示空缓冲区的个数,初值为k
//full:有数据的缓冲区个数,初值为0
//mutex:互斥访问临界区的信号量,初值为1

int  mutex=1, empty=k, full=0,  i=0, j=0;    
DataType  array[k];    

Producer:
        … 
        produce a product x;
        //下面两句发生了互换
        P(mutex); 	    //申请进入缓冲区
        P(empty);    	//申请一个空缓冲
        
        array[i] = x;   	//放入产品
        i = (i+1)mod k;
        V(full);  	// 有数据的缓冲区个数加1
        V(mutex); 	//退出缓冲区
…


 Consumer:
          …
          P(full); 	//申请一个产品
          P(mutex); 	//申请进入缓冲区
          y = array[j]; 	//取产品
          j = (j+1)mod k;
	     V(empty); 	//释放1个空缓冲
         V(mutex); 	//退出缓冲区
  1. 当缓冲区满时,生成者将在P(empty)上等待,但不释放对缓冲区的互斥使用权。
  2. 此后,消费者欲取产品时,由于申请使用缓冲区不成功,它将在P(mutex)上等待。
  3. 相互等待就会造成系统发生死锁现象。

产生死锁的必要条件:

  1. 互斥条件。独占性的资源。
  2. 保持和等待条件。进程因请求资源而阻塞时,对已经获得的资源保持不放。
  3. 不剥夺条件。已分配给进程的资源不能被剥夺,只能由进程自己释放。
  4. 循环等待条件。存在一个进程循环链,链中每个进程都在等待链中的下一个进程所占用的资源。

产生死锁的根本原因:是对独占资源的共享,并发执行进程的同步关系不当。

解决死锁的常见办法:

  1. 鸵鸟算法。忽略死锁。
  2. 死锁的预防。通过破坏产生死锁的四个必要条件中的一个或几个,来防止发生死锁。
  3. 死锁的避免。是在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。
  4. 死锁的检测和恢复。允许死锁发生,通过设置检测机构,及时检测出死锁的发生,然后采取适当措施清除死锁。



3、自旋锁

  自旋锁(spin lock)是一种非阻塞锁,也就是说,如果某进程需要获取锁,但该锁已经被其他进程占用时,该进程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取锁。

  上面提到的互斥量(mutex)是阻塞锁,当某进程无法获取锁时,该进程会被直接挂起,该进程不再消耗CPU时间,当其他进程释放锁后,操作系统会激活那个被挂起的进程,让其投入运行。

为什么要使用自旋锁?
  互斥锁有一个缺点,他的执行流程是这样的 托管代码 - 用户态代码 - 内核态代码、上下文切换开销与损耗,假如获取到资源锁的进程A立马处理完逻辑释放掉资源锁,如果是采取互斥的方式,那么进程B从没有获取锁到获取锁这个过程中,就要用户态和内核态调度、上下文切换的开销和损耗。所以就有了自旋锁的模式,让进程B就在用户态一直循环等待着(会消耗CPU的时间),看是否能够等到A释放了资源的锁,减少消耗。

  自旋锁比较适用于使用者保持锁时间比较短的情况,这种情况下自旋锁的效率要远高于互斥锁。

  自旋锁可能潜在的问题:过多占用CPU的资源,如果锁持有者进程A一直长时间的持有锁处理自己的逻辑,那么这个进程B就会一直循环等待过度占用cpu资源。



4、总结

  文章中提到的进程,对于线程来说也同样适合。

  • 0
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值