文章目录

  • 1. 基本结构
  • 2. P,V操作
  • 3. 信号量的应用
    • 3.1 信号量实现进程互斥
    • 3.2 信号量实现前驱关系
  • 4. 用信号量解经典同步问题
    • 4.1 生产者消费者问题
    • 4.2 读者写者问题
    • 4.3 狒狒过桥问题
    • 4.4 理发师理发问题
    • 4.5 哲学家进餐问题

信号量机制是Dijkstra提出的一种卓有成效的进程同步工具。信号量有整形信号量、记录型信号量、AND型信号量等,这里主要介绍我们常见的记录型信号量。

1. 基本结构

typedef struct {int value;  //信号量值struct process_cntrol_block *list;  //阻塞队列
}semaphore;

在应用信号量的时候,信号量的值往往是临界资源的数量。当临界资源数量不足时,新的进程就阻塞,并插入到信号量阻塞队列中。

2. P,V操作

wait(S)和signal(S)操作是信号量机制的基本操作(通常称作P,V操作),定义如下:

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的初值设定为临界资源的初始数量,一旦进程要请求一个临界资源,先对代表此进程的信号量执行P操作,也就意味着资源数减一,当该进程运行完毕时,执行V操作,意味着资源数加一。值得注意的是,如果当前资源数量为0,再有进程想请求临界资源,就会在执行P操作时进入阻塞队列,信号量值变为-1,此后执行P操作的进程也都会被阻塞,信号量的绝对值为阻塞的进程数目。

3. 信号量的应用

3.1 信号量实现进程互斥

有了P,V操作,我们就能很简单的实现进程互斥。方法是:设mutex为互斥信号量,初值为1。在需要互斥的临界区前后分别使用P,V操作即可,示意代码如下:

semaphore mutex = 1;  //一般初值为1的信号量用来实现互斥
P_A() {  //进程Awhile(1) {P(mutex);临界区;V(mutex);剩余区;}
}
P_B() {  //进程Bwhile(1) {P(mutex);临界区;V(mutex);剩余区;}
}

在互斥问题中,我们可以把P操作理解成“上锁”,把V操作理解成“解锁”,当P_A和P_B两个进程并发执行时,无论哪一个进程先执行到了P操作,都会接下来执行P操作的进程阻塞,直到前一个进程执行到了V操作为止,这样就实现了临界区的互斥。

3.2 信号量实现前驱关系

思路如下:为每一组想要实现前后关系的进程,都分别定义一个信号量,初值设为0,把你想要先执行的进程后面加一个V操作,想要后执行的进程前面加一个P操作。这样一来,你想要后执行的进程如果先执行了,就会因为执行了其前面的P操作而阻塞,直到你想要先执行的进程执行完了,执行V操作后,才能解除阻塞继续执行。以上就是用信号量实现前驱关系的过程。
假如我们要实现四个语句S1,S2,S3,S4需要按照一定的顺序同步执行,比如S1执行完S2,S3才能执行,S2,S3执行完了S4才能执行,代码如下:

p1() {S1; V(a); V(b);}
p2() {P(a); S2; V(c);}
p3() {P(b); S3; V(d);}
p4() {P(c); P(d); S4;}main() {semaphore a,b,c,d = 0;cobeginp1();p2();p3();p4();coend
}

4. 用信号量解经典同步问题

4.1 生产者消费者问题

假定生产者和消费者之间的公用缓冲池有n个缓冲区,生产者可以向缓冲区生产一个产品,消费者可以从缓冲区消耗一个产品,注意:生产者不能同时生产,消费者也不能同时消费,也不能同时生产和消费,当缓冲区空时无法消费,当缓冲区满时无法生产。请用信号量机制解决这一问题。
解法如下:

semaphore mutex = 1;  //实现生产与消费、生产与生产、消费与消费
semaphore full = 0, empty = n;  //full表示已用缓冲区数,empty表示空缓冲区数
void producer() {while(1) {P(empty);  //每次生产会减少一个空缓冲区,如果没有空缓存区了就阻塞P(mutex);  //保证互斥生产一个产品;V(mutex);  //保证互斥V(full);  //每次生产增加一个已用缓冲区}
}
void consumer() {while(1) {P(full);  //每次消费会减少一个已用缓冲区,如果没有已用缓存区了就阻塞P(mutex);消耗一个产品;V(mutex);V(empty);  //每次生产增加一个空缓冲区}
}

4.2 读者写者问题

假设一个文件可被多个进程共享,我们允许多个进程同时读这个共享对象,但是不允许一个进程写这个共享对象的同时,别的进程进行读或写。换句话说:读和读不互斥,读和写互斥,写和写互斥。请用信号量机制解决这一问题。
解法如下:

semaphore rmutex, wmutex = 1; //wmutex用以实现写进程与其它进程的互斥
int readcount = 0;  //记录读进程的数量
void reader() {while(1) {P(rmutex);  //见下文解释if(readcount == 0) P(wmutex);  //第一个读进程去把写进程上锁readcount++;  V(rmutex);  //见下文解释读者读;P(rmutex);  //见下文解释readcount--;if(readcount == 0) V(wmutex);  //解锁V(rmutex);  //见下文解释}
}
void writer() {while(1) {P(wmutex);  //实现写进程与其他进程都互斥写者写;V(wmutex);}
}

这个问题的程序中有个关键点,就是明明读进程是不互斥的,为什么还需要rmutex来实现if判断的互斥呢。原因如下:当我们在进程互斥中使用条件语句和数值变化的时候,如果不把那一段也实现互斥的话,很有可能出问题,比如在这段代码中,要是不加第5行和第8行的话:如果一个读进程执行完了第6行,还没有执行第7行的readcount++操作之前,这个读进程被剥夺了处理机,换另一个读进程上处理机运行了,那么另一个读进程就会阻塞在第6行的P操作上,就无法实现多个读者一起读了;10行和13行同理,如果一个读进程执行完了第11行就被剥夺处理机,换上另一个读进程也执行了11行,这样readcount被连减两次,那么这两个进程都能通过12行的if判断,会执行两次V操作,产生错误。

4.3 狒狒过桥问题

一个主修人类学、辅修计算机科学的学生参加了一个课题,调查非洲狒狒是否能被教会理解死锁。他找到一处很深的峡谷,在上边固定了一根横跨峡谷的绳索,这样狒狒就可以攀住绳索越过峡谷。同一时刻可以有几只狒狒通过,只要它们朝着相同的方向。但如果向东和向西的狒狒同时攀在绳索上则将发生死锁(狒狒将被卡在中间),因为它们无法在吊在峡谷上时从另一只的背上翻过去。如果一只狒狒想越过峡谷,它必须看当前是否有别的狒狒正在逆向通过。使用信号量写一个避免死锁的程序来解决该问题。
解法如下:

semaphore wmutex, emutex, mutex = 1;
int wcount, ecount = 0;  //分别记录东西狒狒上绳索的个数
void west_monkey {while(1) {P(wmutex);  //使if语句互斥if(wcount == 0) P(mutex);  //第一个西狒狒给东狒狒上锁wcount++;V(wmutex);  //使if语句互斥西狒狒过桥;P(wmutex);wcount--;if(wcount == 0) V(mutex);V(wmutex);}
}
void east_monkey {  //和上面的一样while(1) {P(emutex);if(ecount == 0) P(mutex);ecount++;V(emutex);西狒狒过桥;P(emutex);ecount--;if(ecount == 0) V(mutex);V(emutex);}
}

该问题属于读者写者问题的改进,如果完全理解了读者写者问题的解法,那么这个问题也能很快解决。东狒狒之间不互斥,西狒狒之间不互斥,东西狒狒之间互斥,思路是第一个东狒狒给西狒狒上锁,第一个西狒狒给东狒狒上锁,注意if判断也要实现互斥。

4.4 理发师理发问题

理发店里有一位理发师、一把理发椅和n把供等候理发的顾客坐的椅子。如果没有顾客,则理发师便在理发椅上睡觉。当一个顾客到来时,他必须先叫醒理发师,如果理发师正在理发时又有顾客来到,则如果有空椅子可坐,他们就坐下来等。如果没有空椅子,他就离开。这里的问题是为理发师和顾客各编写一段程序来描述他们的行为,要求不能带有竞争条件。
解法如下:

semaphore customer, barber = 0;  //一开始没有顾客,理发师也是睡着的
semaphore mutex = 1;  //互斥信号量
int empty = N;  //空椅子数量为N
void Barber() {while(1) {P(customer);  //只有顾客进程的V执行后才能执行,没有顾客就阻塞(睡觉)P(mutex);  //把数量的变化实现互斥,以免影响到顾客进程的if判断语句empty++;  //椅子上的顾客起身V(barber);  //有了一个理发师可以理发V(mutex);理发;}
}
void Customer() {P(mutex);if(empty > 0) {empty--;  //不管是不是第一个顾客,来了先得坐凳子上,因为理发师在理发椅上睡觉呢V(customer);  //增加一个顾客,唤醒沉睡的理发师V(mutex); P(barber);  //消耗一个理发师,没有理发师就阻塞理发;} else {V(mutex);离开;}
}

这道题本身不难,但其中很多细节的实现需要实现,比如座位是有上限的,这样就不得不设置判断条件和计数,来使超过N个的顾客离开,而加入计数和判断条件后就又要实现其互斥,增加了问题的复杂性。

4.5 哲学家进餐问题

五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在桌子上有五只碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。请用信号量机制解决。
这是一个讲解死锁的时候的经典例子,解决这道题的直接思路如下:

semaphore chopstick[5] = {1,1,1,1,1};
void philosopher() {while(1) {/*当哲学家饥饿时,总是先拿左边的筷子,再拿右边的筷子*/P(chopstick[i]);P(chopstick[(i+1)%5]);吃饭;V(chopstick[i]);V(chopstick[(i+1)%5]);}
}

但是这样可能会出现死锁问题:如果所有哲学家都拿起了左手边筷子,五个进程就会死锁。对于避免哲学家进餐问题发生死锁的方法有很多,这里只讲一种:只有当同时一个哲学家能同时拿起左右两只筷子时,才允许拿筷子,解法如下:

semaphore chopstick[5] = {1,1,1,1,1};
semaphore mutex = 1;
void philosopher() {while(1) {P(mutex);P(chopstick[i]);P(chopstick[(i+1)%5]);V(mutex);吃饭;V(chopstick[i]);V(chopstick[(i+1)%5]);}
}

但仅仅这样做还是有问题,如果一个哲学家获得了两只筷子,开始进餐,此时他左边或者右边的哲学家进入了临界区后,被阻塞在第6行或者第7行,那么其他的哲学家就无法进入临界区了,也就是说这个时间只能有一个哲学家进餐,但显然同一时间是可以有不相邻的两个哲学家同时进餐的。
对这个问题的更好解决方法可以去看Dijkstra在1965年给出的算法。

【操作系统】信号量解决经典同步问题相关推荐

  1. 计算机操作系统感悟随笔--经典同步问题

    ** 1.生产者-消费者问题(The proceducer – consumer problem:多个生产者和消费者对n个缓冲区的使用.) ** 1.无论生产者.消费者使用缓冲池时应保证互斥使用(互斥 ...

  2. 操作系统——信号量(理解什么是信号量,信号量如何解决同步互斥问题,信号量一些注意点)

    信号量是什么 信号量(semaphore)是操作系统用来解决并发中的互斥和同步问题的一种方法. 信号量是一个与队列有关的整型变量,你可以把它想象成一个数后面拖着一条排队的队列,如图: 那信号量上面值n ...

  3. 操作系统课设--使用信号量解决生产者/消费者同步问题

    山东大学操作系统课设lab3 实验三 使用信号量解决生产者/消费者同步问题(lab3) 实验目的 理解Nachos的信号量是如何实现的 生产者/消费者问题是如何用信号量实现的 在Nachos中是如何创 ...

  4. 用信号量解决进程的同步与互斥

    转自:http://www.cnblogs.com/whatbeg/p/4435286.html 现代操作系统采用多道程序设计机制,多个进程可以并发执行,CPU在进程之间来回切换,共享某些资源,提高了 ...

  5. 进程的同步和互斥反映了_用信号量解决进程的同步与互斥探讨【持续更新】

    现代操作系统采用多道程序设计机制,多个进程可以并发执行,CPU在进程之间来回切换,共享某些资源,提高了资源的利用率,但这也使得处理并发执行的多个进程之间的冲突和相互制约关系成为了一道难题.如果对并发进 ...

  6. 信号量解决进程的同步和互斥

    转自:http://www.cnblogs.com/whatbeg/p/4435286.html 现代操作系统采用多道程序设计机制,多个进程可以并发执行,CPU在进程之间来回切换,共享某些资源,提高了 ...

  7. 用信号量解决进程的同步与互斥探讨【持续更新】

    现代操作系统采用多道程序设计机制,多个进程可以并发执行,CPU在进程之间来回切换,共享某些资源,提高了资源的利用率,但这也使得处理并发执行的多个进程之间的冲突和相互制约关系成为了一道难题.如果对并发进 ...

  8. 用信号量解决进程的同步与互斥探讨

    <p>本篇也是一篇老文,发布于2015年5月,文章比较长,算老博客看的比较多的了,贴到这儿与大家分享,以求多多交流探讨.</p> 前言 现代操作系统采用多道程序设计机制,多个进 ...

  9. 写出记录型信号量中的wait操作代码_操作系统进程的同步与互斥及经典同步与互斥问题...

    概览 临界区临界区的引入 在系统当中,有些资源允许多个进程共享(磁盘),有些资源只允许进程单独使用(打印机,共享变量).为了让进程单独使用资源而不受其他进程干扰引入了临界区的概念. 临界区的概念 在一 ...

最新文章

  1. 微信小程序----map组件实现(获取定位城市天气或者指定城市天气数据)
  2. mac osx 上Eclipse/CDT问题及解决方案
  3. python 格式化输出%和format
  4. C# | C#中变量(局部变量)的声明和范围
  5. haproxy5-ssl
  6. 还在纠结蓝牙耳机哪款好用吗?2020我推荐这几款高性价比蓝牙耳机
  7. Ubuntu 解锁被锁定的账号
  8. Twaver-HTML5基础学习(24)快速查找(QuickFinder)
  9. 百度服务获取坐标放置在天地图上实例
  10. 关于职场,你需要不断拼搏
  11. Linux常用命令--压缩解压命令
  12. Windows 10的最新版本是什么?
  13. 【LoVissy学习笔记】用Python3.82自动发送邮件。QQ邮箱
  14. 全屋定制“下乡”之路,一线从业者的思考:这真是赚钱的生意吗?
  15. SimpleDateFormat格式化日期
  16. opencv_python清华镜像下载地址
  17. GIT仓库(本地仓库)
  18. B站石群老师《电路》学习笔记1
  19. 苹果cms php免费资源,苹果CMS自动采集 X站源码带全部资源PHP - 下载 - 搜珍网
  20. 神经网络结构的缺点是,各种神经网络的优缺点

热门文章

  1. 关于源码,反码,补码(正数--负数)---------(-128)自己的理解
  2. 网页性能优化(初窥)
  3. 一起动手打造个人娱乐级linux
  4. [导入]【翻译】WF从入门到精通(第十章):事件活动
  5. C#与VB.NET代码互相转换在线工具
  6. java thread isalive_Java线程编程中isAlive()和join()的使用详解
  7. java jni helloword_JNI入门教程之HelloWorld篇
  8. clistctrl控件最后插入在后面_老板让我把图片放到Excel表格中,批量插入效率高...
  9. 新型发明创造大赛计算机类,2017年发明杯全国高职高专大学生创新创业大赛
  10. java婚庆网站源码_基于jsp的婚庆网站-JavaEE实现婚庆网站 - java项目源码