进程的同步与互斥

一,小知识

什么是临界资源?
  • 系统中的某些资源一次只允许一个进程使用的资源临界资源称之为临界资源或者互斥资源共享资源。各进程采取互斥的方式,实现共享的资源称作临界资源。属于临界资源的硬件有,打印机,磁带机等;软件有消息队列,变量,数组,缓冲区等。诸进程间采取互斥方式,实现对这种资源的共享。
  • 每个进程中对临街资源实施操作的代码段称为临界区(criticalsection),每次只允许一个进程进入临界区,进入后,不允许其他进程进入。不论是硬件临界资源还是软件临界资源,多个进程必须互斥的对它进行访问。
临界区的使用规则
  • 空闲让进:临界资源空闲时一定要让进程进入,不发生“互斥礼让”行为。
  • 忙则等待:临界资源正在使用时外面的进程等待。
  • 有限等待:进程等待进入临界区的时间是有限的,不会发生“饿死”的情况。
  • 让权等待:进程等待进入临界区是应该放弃CPU的使用。
什么是原子操作
  • 不可中断的一个或者一系列操作

二,进程同步

概念
  • 指系统中多个进程中发生的事件存在某种时序的关系,需要相互合作,共同完成一项任务。如进程A产生数据,而进程B打印数据,则B在打印之前必须等A产生数据
  • 这也是保证进程对临界资源访问的时序可控性,
  • 进程同步是进程间的一种合作关系,指的是“我们大家利用一些共同的资源区,大家一起合作,完成某些事情,但是我在干某些小事的时候,可能要等到你做完另一些小事”,是一种源于相互合作的直接制约关系。
  • 同步的进程间是有必然联系的,即使竞争到使用权,如果合作者没有发出必要的信息,该进程依然不能执行。就比如排队打水,即使排到你了,如果水箱没水了,你就打不了水,说明你和水箱是有着必然联系的,你得从它里面取水,你们是同步关系,你们合作完成“打水”这个过程。

三,进程互斥

概念
  • 意思是两个进程间存在竞争关系,进程A和进程B共同竞争临界资源,当进程A得到临界资源的使用权的时候,B不得在访问临界资源,直到A使用完了之后,B才会得到该临界资源的使用权
  • 进程互斥是保证对临界资源同一时间的唯一访问性
  • 进程互斥说白了就是“你用的时候别人都不能用,别人用的时候,你也不能去用”,是一种源于资源共享的间接制约关系。
  • 互斥的进程间没有必然的联系,属于竞争者关系,谁竞争到资源(的使用权),谁就使用它,直到使用完才归还。就比如洗衣房的洗衣机这个资源,去洗衣的同学并不需要有必然联系,你们可以互不认识,但是谁竞争到洗衣机的使用权,就可以使用,直到洗完走人

四,信号量

引入信号量
  • 荷兰计算机科学家Dijkstra于1965年提出了解决进程同步与互斥问题的信号量机制,收到了很好的效果,被一直沿用至今,广泛应用与单处理机和多处理机系统以及计算机网络中。
  • 信号量机制就是说两个或者多个进程通过他们都可以使用的一个或多个信号来实现准确无误不冲突的并发执行。如果临界资源不够,就会有一个信号表示出来,如果进程此时想访问,那么就会阻塞到一个队列中,等待调度。当临界资源使用完毕,一个进程改变信号,并及时唤醒阻塞的进程,这就实现了进程间的同步和互斥问题。
概念
  • 信号量是一个特殊的变量,他只能取自然数值并且只支持两种操作:wait和signal,因为在linux中wait和signal已经有了含义,所有信号量对这两个操作更常用的称呼是P,V操作,含义就是P(进入临界区), V(退出临界区),
  • 每一个信号量都有一个非负的初值,反映当前资源的数量,也表示可以访问临界资源的进程个数(用来实心同步和互斥)
  • 假设有信号量SV,对信号量的PV操作有如下含义:
    P(SV):如果一个进程执行P(SV)操作,先检查SV的值,如果大于0就执行SV减一,否则该线程要挂起等待
    V(SV):如果一个进程执行V(SV)操作,如果有其他线程因为等待SV而挂起,就唤醒,否则SV加一
  • 在这里插入图片描述
  • 上图中的SV是一个二元信号量,二元信号量的初值为1,进程A和进程B同时竞争访问临界区的资源,进程A得到了访问临界区的权利,A进程执行P(SV),先检查SV的值,SV的值为1,对SV进行减一操作,然后进入临界区,此时进程B也执行P(SV),检测SV的值是0,就挂起等待。
  • 当进程A出临界区的时候,执行V(SV),直接对SV加一,此时临界区变得重新可用,进程B在执行P(SV),就可以进入临界区进行操作了。
分类
  • 整型信号量—就是一个整型的数值
  • 记录型信号量—二元组(包含一个value值和一个等待队列)
  • AND信号量
  • 信号量集
原理
  • 相当于内核中具有等待队列的计数器,用于资源计数,没资源就等待,有资源就唤醒
  • P操作相当于“等待一个信号”,而V操作相当于“发送一个信号”,在实现同步过程中,V操作相当于发送一个信号说合作者已经完成了某项任务,在实现互斥过程中,V操作相当于发送一个信号说临界资源可用了。实际上,在实现互斥时,P,V操作相当于申请资源和释放资源。
  • 主要针对记录型信号量来说,在底层是一个数据结构,一个二元组,含有两个成员,一个value一个等待队列,
  • 当一个进程执行P操作,先对当前value减一,再判断value的值,如果小于0,说明之前的value小于1,说明现在没有资源可用,直接把该进程加入等待队列中,此时|value|表示等待队列的长度
  • 当一个进程执行V操作时,直接对当前的value加一,再判断value,如果value小于0,说明此时有线程等待该信号量,就唤醒线程,如果大于0,表示无线程等待信号量,就什么也不做,

五,经典问题

问题一 :生产者消费者模型
双方的关系
  • 两个进程对同一个内存资源进行操作,一个是生产者,一个是消费者。
  • 生产者往共享内存资源填充数据,如果区域满,则等待消费者消费数据。
  • 消费者从共享内存资源取数据,如果区域空,则等待生产者填充数据。
  • 生产者的填充数据行为和消费者的消费数据行为不可在同一时间发生。
    在这里插入图片描述
对双方关系的解读
  • 双方的同步关系
    • 当缓冲区数据满了的时候,生产者等待消费者取出数据,
    • 当缓冲区数据为空的时候,消费者等待生产者产生数据,双方合作完成数据的转移任务
  • 双方的互斥关系
    • 在生产者生产数据的时候,消费者不能取出数据
    • 在消费者取出数据的时候,生产者不能产生数据,
设计代码
  • 由于有互斥关系,所以我们应该设置一个互斥量mutex控制两者不能同时操作缓冲区。
  • 为了控制同步关系,我们设置两个信号量empty和full来表示缓冲区的空槽数目和满槽数目,即有数据的缓冲区单元的个数。mutex初值为1,empty初值为n,即缓冲区容量,代表初始没有任何数据,有n个空的单元,类似的,full初值为0,表示初始情况下,数据满了的单元为0;
void Productor() {
    while(1) {
        //制造数据
        P(&empty);//再要制造数据之前,先判断一下缓冲区的为空的数据单元个数,如果大于0,就进入缓冲
                      // 区写数据,如果等于0 表示没有空的数据单元,该进程就应该等待,
        P(&mutex);//这个也可以理解为一个信号量,表示可以访问该资源的进程数目,初始值为1,保证互斥
        //填充数据
        V(&mutex);//当一个进程退出缓冲区,表示可以访问缓冲的进程数目又回到一个
        V(&full);//当一个进程完成数据制造的时候,满的数据单元应该加一
    }
}

void Consumer() {
    while(1) {
        P(&full);//判断数据满的单元是否为0,如果是0就不能读取数据,只能等待有数据了才能读取数据
        P(&mutex);//加锁,保证同一时间只能有一个进程对数据操作
        //消费数据
        V(&mutex);//解锁
        V(&empty);//因为读取了一个单元的数据,因此数据为空的单元格应该加一
    }
}
  • 在以上操作中,有两个P操作和两个V操作,V操作的顺序可以互换,但是P操作的顺序不能互换,如果先加锁,在判断空的单元格的话,很容易造成死锁,比如,现在先加锁,在判断空的单元格,如果空的单元格为空,该进程应该等待,然后就没人解锁了,从而导致死锁,
  • 两个V操作都是解锁和释放空间,不涉及加锁,因此不会有这个问题,
问题一 :读者—写者模型(读写锁)
  • 两者关系描述
    • 一个进程读共享资源的数据的时候,其他进程也可以读数据
    • 一个进程给共享资源写数据的时候,其他进程不能写也不能读
    • 一个进程在读数据的时候,其他进程不能写数据
  • 对双方关系的解读
    • 读者和写者是互斥关系
  • 设计代码
    • 因为只有互斥关系,先创建一个信号量mutex,初始值设为1,来用做保证互斥的,
    • 如果只实现一个简单的信号量的话,本来读者和读者之间没有互斥关系,现在也出现了互斥关系,这是我们不愿看到的,因此需要一个变量来记录读者的个数
    • 所以我们设置一个变量ReadCount表示读者的数量,好,这个时候,对于ReadCount又要实现多个读者对他的互斥访问,所以还要设置一个RC_mutex。来保证每一个读者对这个表示读者数量的变量的互斥性。
void Reader() {
    while(1) {
        P(&RC_mutex);//每一个读者要保证对rc变量访问的互斥性
        rc = rc + 1;
        if(rc == 1) P(&mutex);  //如果是第一个读者,那么限制写者的访问
        V(&RC_mutex);//解锁
        //读数据
        P(&RC_mutex);
        rc = rc - 1;
        if(rc == 0) V(&mutex);  //如果是最后一个读者,那么释放以供写者或读者访问
        V(&RC_mutex);
    }
}

void Writer() {
    while(1) {
        P(&mutex);
        //写数据
        V(&mutex);
    }
}

六,小结

  • 至此,我们已经可以总结出一点用信号量解决同步互斥问题的基本规律和一般步骤:
  • 分析各进程间的制约关系,从而得出同步与互斥关系
  • 根据上一步中的分析,设置信号量
  • 编写伪代码,实施P,V操作
  • 同步:多个进程在执行次序上的协调,相互等待消息 互斥:对临界资源的使用
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值