信号量是一种变量类型,用一个记录型数据结构表示,有两个分量:信号量的值和信号量队列指针
除了赋初值外,信号量仅能通过同步原语PV对其进行操作
s.value为正时,此值为封锁进程前对s信号量可施行的P操作数,即s代表实际可用的物理资源
s.value为负时,其绝对值为对信号量s实施P操作而被封锁并进入信号量s等待队列的进程数,即登记排列在s信号量队列之中等待的进程个数
s.value为0时,无资源且无进程等待
信号量按其取值可分为二值信号量和一般信号量(计数信号量),记录型信号量和PV操作定义为如下数据结构和不可中断过程:
  1. typedef struct semaphore {
  2. int value;
  3. struct pcb *list; //信号量队列指针
  4. }
  5. void P(semaphore s){
  6. s.value--;
  7. if(s.value<) sleep(s.list); //若信号量值小于0,执行P操作的进程调用sleep(s.list)阻塞自己,被置成等待信号量s状态并移入s信号量队列,转向进程调度程序
  8. }
  9. void V(semaphore s){
  10. s.value++;
  11. if(s.value<=) wakeup(s.list); //若信号量小于等于0,则调用wakeup(s.list)从信号量s队列中释放一个等待信号量s的进程并转换成就绪态,进程则继续运行
  12. }
二值信号量虽然仅能取值0、1,但它和其它记录型信号量有一样的表达能力
  • 机票问题
注意:(1)P操作与V操作在执行路径上必须一一对应,有一个P操作就有一个V操作;(2)输出一张票的操作不应放在临界区内
  1. int A[m];
  2. Semaphore s = ;
  3. cobegin
  4. process Pi {
  5. int Xi;
  6. Li:按旅客定票要求找到A[j];
  7. P(s);
  8. Xi = A[j];
  9. If (Xi>=) { Xi=Xi-; A[j]=Xi;V(s); 输出一张票;}
  10. else {V(s); 输出票已售完;}
  11. goto Li;
  12. }
  13. coend;
只有相同航班的票数才是相关的临界资源,所以用一个信号量s处理全部机票会影响进程并发度
可以让每一个航班都有自己的临界区,把信号量改为s[m]
  1. int A[m];
  2. Semaphore s[m]; //每一个航班都有自己的临界区
  3. For (int j=;j<m;i++) s[j] = ;
  4. cobegin
  5. process Pi {
  6. int Xi;
  7. L1:按旅客定票要求找到A[j];
  8. P(s[j]);
  9. Xi = A[j];
  10. If (Xi>=) { Xi=Xi-; A[j]=Xi;V(s[j]); 输出一张票;
  11. } else {V(s[j]); 输出票已售完;}
  12. goto L1;
  13. }
  14. coend;
  • 生产者消费者问题
(1)1生产者1消费者1缓冲区问题
  1. int B;
  2. semaphore sput = /* 可以使用的空缓冲区数 */
  3. semaphore sget = ; /* 缓冲区内可以使用的产品数 */
  4. process producer {
  5. L1:
  6. produce a product
  7. P(sput);
  8. B = product
  9. V(sget);
  10. goto L1;
  11. }
  12. process consumer {
  13. L2:
  14. P(sget);
  15. product = B
  16. V(sput);
  17. consume a product
  18. goto L2;
  19. }
(2)1生产者1消费者N缓冲区问题
必须引入放入和取出产品的循环队列指针putptr指针和getptr指针进行管理,因为只有1生产者1消费者所以它们不需要是共享变量
  1. int B[k]; // 共享缓冲区队列
  2. semaphore sput = N //可以使用的空缓冲区数
  3. semaphore sget = ; //缓冲区内可以使用的产品数
  4. int putptr = getptr = ;
  5. process producer {
  6. L1:produce a product
  7. P(sput);
  8. B[putptr] = product
  9. putptr = (putptr + ) mod k;
  10. V(sget);
  11. goto L1;
  12. }
  13. process consumer {
  14. L2:P(sget);
  15. product = B[getptr];
  16. getptr = (getptr + ) mod k;
  17. V(sput);
  18. consume a product
  19. goto L2;
  20. }
️(3)N生产者N消费者N缓冲区问题
必须引入新的信号量s1和s2对putptr指针和getptr指针进行管理
  1. int B[k];
  2. semaphore sput = N /* 可以使用的空缓冲区数 */
  3. semaphore sget = ; /* 缓冲区内可以使用的产品数 */
  4. int putptr = getptr = ;
  5. semaphore s1 = s2 = /* 互斥使用putptr、getptr */
  6. process producer_i {
  7. L1:produce a product
  8. P(sput);
  9. P(s1);
  10. B[putptr] = product
  11. putptr = ( putptr + ) mod k;
  12. V(s1);
  13. V(sget);
  14. goto L1;
  15. }
  16. process consumer_j {
  17. L2:P(sget);
  18. P(s2);
  19. Product = B[getptr];
  20. getptr = ( getptr + ) mod k;
  21. V(s2);
  22. V(sput);
  23. consume a product
  24. goto L2;
  25. }
若要求一个消费者取10次,其它消费者才能开始取,则需要再增加一个信号量mutex1并对消费者进程进行改造,循环10次后再V(mutex1)。
这里通过一个互斥信号量mutex2互斥访问缓冲区,而不是像前面那样通过两个互斥信号量s1和s2分别互斥使用缓冲区存指针putptr、取指针getptr
  1. process consumer_j {
  2. P(mutex1);
  3. for(int i=; i<; i++){
  4. P(full);
  5. P(mutex2);
  6. 互斥访问缓冲区;
  7. V(mutex2);
  8. V(empty);
  9. }
  10. V(mutex1);
  11. }
(4)苹果橘子问题
父亲只放评估、母亲只放橙子;儿子只吃橙子、女儿只吃苹果
同步关系1:有苹果
同步关系2:有橘子
同步关系3:有空位
  1. Semaphore sp /* 盘子里可以放几个水果*/
  2. Semaphore sg1; /* 盘子里有桔子*/
  3. Semaphore sg2; /* 盘子里有苹果*/
  4. sp = ; /* 盘子里允许放入一个水果*/
  5. sg1 = ; /* 盘子里没有桔子*/
  6. sg2 = ; /* 盘子里没有苹果*/
  7. process father {
  8. L1: 削一个苹果;
  9. P(sp);
  10. 把苹果放入plate
  11. V(sg2);
  12. goto L1;
  13. }
  14. process mother {
  15. L2: 剥一个桔子;
  16. P(sp);
  17. 把桔子放入plate
  18. V(sg1);
  19. goto L2;
  20. }
  21. process son {
  22. L3: P(sg1);
  23. plate中取桔子;
  24. V(sp);
  25. 吃桔子;
  26. goto L3;
  27. }
  28. process daughter {
  29. L4: P(sg2);
  30. plate中取苹果;
  31. V(sp);
  32. 吃苹果;
  33. goto L4;
  34. }
(5)吸烟者问题
三个消费者各有一种材料、缺另外两种材料,供应者每次随机供应两种不同材料
想法:生产者每次供应两种不同材料,其实可以理解为有三种不同的生产材料提供方式,分别供三种消费者消费
  1. int random;
  2. Semaphore offer1 = offer2 = offer3 =
  3. Semaphore finish = ;
  4. process producer(){
  5. while(){
  6. random = 任意随机整数;
  7. random = random %
  8. P(finish);
  9. 对应两种材料放在桌子上;
  10. if(random==) V(offer1);
  11. else if(random==) V(offer2);
  12. else V(offer3);
  13. }
  14. }
  15. process consumer_1(){
  16. while(){
  17. P(offer1);
  18. 拿自己缺的那两种材料;
  19. 卷成烟抽掉;
  20. V(finish);
  21. }
  22. }
(6)1生产者2消费者N缓冲区,生产者随机生成正整数,2个消费者分别取奇数和偶数
思路:这个问题和抽烟者问题很类似,关键是要定义缓冲区里有奇数的互斥信号量odd、缓冲区里有偶数的互斥信号量even。每次生产者随机生成一个数之后,判断奇偶,分别V(odd)和V(even)
  • 哲学家就餐问题
  1. Semaphore fork[];
  2. for (int i = ; i < ; i++)
  3. fork[i] = ;
  4. cobegin
  5. process philsopher_i() {
  6. while (true) {
  7. think();
  8. P(fork[i]);
  9. P(fork[(i+)%]);
  10. eat();
  11. V(fork[i]);
  12. V(fork[(i+)%]);
  13. }
  14. }
  15. coend
如果5位哲学家同时拿起他们左手/右手的叉子,将出现死锁,可以:
(1)至多允许四个哲学家同时拿叉子
(2)奇数号哲学家先取左边叉子,再取右边叉子;偶数号哲学家则相反
(3)每位哲学家取到手边两把叉子才开始吃,否则一把也不取:可以通过引入互斥信号量mutex每次只允许一个哲学家拿叉子
  1. Semaphore fork[];
  2. for (int i = ; i < ; i++)
  3. fork[i] = ;
  4. Semaphore mutex = ;
  5. cobegin
  6. process philsopher_i() {
  7. while(true){
  8. P(mutex);
  9. P(fork[i]);
  10. P(fork[(i+)%]);
  11. V(mutex);
  12. eat();
  13. V(fork[i]);
  14. V(fork[(i+)%]);
  15. }
  16. }
  17. coend
  • 写者问题
一次只允许一个写者写,但可以N个读者同时读。写者完成写操作前不允许其它写者、读者操作
引入表示是否允许写的信号量writeblock,相当于任何进程在工作的时候都不允许写。不过单纯引入信号量不能解决此问题,还必须引入计数器readcount对读进程进行计数,读之前检查计数器,如果为1(自己是唯一的读进程)才需要P(writeblock),读之后检查计数器,如果为0(当前已无读进程)则需要V(writeblock)。
mutex是用于对计数器readcount操作的互斥信号量
  1. int readcount = ;
  2. Semaphore writeblock = ;
  3. Semaphore mutex = ;
  4. cobegin
  5. process read_i() {
  6. P(mutex);
  7. readcount++;
  8. if(readcount==) P(writeblock); //自己是唯一的读进程,写者在写文件时自己不能开始读,自己开始读后不允许写操作
  9. V(mutex);
  10. /*读文件*/
  11. P(mutex);
  12. readcount-—;
  13. if(readcount==) V(writeblock); //自己是唯一的读进程,读文件完成后允许写操作
  14. V(mutex);
  15. }
  16. process write_j() {
  17. P(writeblock);
  18. /*写文件*/
  19. V(writeblock);
  20. }
  21. coend
此写法读者优先,当存在读者时写者将被延迟。且只要有一个读者活跃,随后而来的读者都将被允许访问文件,从而导致写者长时间等待。
改进方法:增加信号量,确保当一个写进程声明想写时,不允许后面的新读者访问共享文件。
  • 男女共浴问题、汽车过桥问题
这个问题的关键是设置一把性别锁mutex,第一个到的男人要负责抢这把锁,因此mutex的PV操作必须放在修改mancount的临界区内。一旦男人抢到了这把锁,试图抢这把锁的女人就会停留在womancount的临界区内出不来,而每次又只允许一个女人进入womancount的临界区。
最后一个走的男人要负责把这把锁释放掉。
  1. Semaphore mutex;
  2. Semaphore mutex_man = mutex_woman= ;
  3. int mancount = womancount =;
  4. Process man(){
  5. P(mutex_man)
  6. mancount++;
  7. if(mancount==) P(mutex);
  8. V(mutex_man);
  9. 洗澡;
  10. P(mutex_man);
  11. mancount—;
  12. if(mancount==) V(mutex);
  13. V(mutex_man)
  14. }
南大18年真题过桥问题:汽车只能单向过桥,最多12辆车同时过桥。
  • 理发师问题
引入一个计数器waiting记录等待理发的顾客坐的椅子数,初值为0最大为N。mutex是用于对计数器waiting操作的互斥信号量
引入信号量customers记录等候理发的顾客数,并用于阻塞理发师进程,初值为0
引入信号量barbers记录正在等候顾客的理发师数,并用于阻塞顾客进程,初值为0
想法:其实和N生产者1消费者N缓冲区的生产者消费者问题有些类似,但是多了P(barbers)和V(barbers);此外,椅子数改用了int数waiting再用信号量mutex进行互斥。
  1. int waiting = ;
  2. Semaphore customers = ;
  3. Semaphore barbers = ;
  4. Semaphore mutex =
  5. cobegin
  6. process barbers() {
  7. while(true) {
  8. P(customers);//判断是否有顾客,没有的话理发师睡眠
  9. P(mutex);
  10. waiting--;
  11. V(barbers);//理发师准备为顾客理发
  12. V(mutex);
  13. cuthair(); //理发师理发,不应放在临界区
  14. }
  15. }
  16. process customer_i() {
  17. P(mutex);
  18. if(waiting<N) {
  19. waiting++;
  20. V(customers);//唤醒理发师
  21. V(mutex);
  22. P(barbers);//如果理发师忙则等待
  23. get_haircut();
  24. }
  25. else V(mutex);//人满了,顾客离开
  26. }
  27. coend

操作系统-PV操作的原理和几种常见问题的更多相关文章

  1. 操作系统实验——PV操作实现生产者消费者模型

    操作系统PV操作之--生产者消费者模型 个人博客主页 参考资料: Java实现PV操作 | 生产者与消费者 浙大公开课 在操作系统的多进程.多线程操作中经常会有因为同步.互斥等等问题引发出的一系列问题 ...

  2. 操作的系统的PV操作

    转自:https://blog.csdn.net/sunlovefly2012/article/details/9396201 在操作系统中,进程之间经常会存在互斥(都需要共享独占性资源时) 和同步( ...

  3. Java实现PV操作 | 读者与写者(在三种情况下进行讨论)

    注 :本文应结合[天勤笔记]进行学习. 1.读者优先 设置rmutex信号量来对readcount变量进行互斥访问.mutex信号量对写者与读者进行同步. static syn rmutex=new ...

  4. 信号量与PV操作

    在计算机操作系统中,PV操作是进程管理中的难点.首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:    P(S):①将信号量S的 ...

  5. 软测试综述——PV操作

     在操作系统中,进程之间常常会存在相互排斥(都须要共享独占性资源时)和同步(完毕异步的两个进程的协作)两种关系.而信号量和PV操作完美有效的处理了这两种情况.     相互排斥:就好比过独木桥,一 ...

  6. <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步(1)

    <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步 并发问题是所有问题的基础,也是操作系统设计的基础.并发包括很多设计问题,其中有进程间通信,资源共享与竞争,多个 ...

  7. 转 信号量与PV操作

    在计算机操作系统中,PV操作是进程管理中的难点.首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:    P(S):①将信号量S的 ...

  8. 【转】进程同步之信号量机制(pv操作)及三个经典同步问题

    原文地址:http://blog.csdn.net/speedme/article/details/17597373 上篇博客中(进程同步之临界区域问题及Peterson算法),我们对临界区,临界资源 ...

  9. OS__信号量(semaphore)PV操作

    信号量的概念 1.信号量的类型定义 信号量(semaphore)的数据结构为记录型数据结构一个值和一个指针,指针指向等待该信号量的下一个进程.信号量的值与相应资源的使用情况有关,在操作系统中,信号量用 ...

随机推荐

  1. Spring Boot AOP的使用

    简单来说讲,动态地将代码切入到类的指定方法.指定位置上的编程思想就是面向切面的编程. 目录 AOP几个术语 AOP Demo 引入Maven依赖 一个简单的Controller 定义切面类 调用服务 ...

  2. bootstrap-treeview 研究一下

    一直以来都是拿来主义,现在正好有空,也正好用到,准备好好研究下bootstrap-treeview. 实现目标:可搜索,可复选选中的权限控制菜单项. 研究失败 转 jstree

  3. PHP array_shift() 函数

    实例 删除数组中的第一个元素(red),并返回被删除的元素: <?php $a=array("a"=>"red","b"=> ...

  4. PHP krsort() 函数

    ------------恢复内容开始------------ 实例 对关联数组按照键名进行降序排序: <?php$age=array("Peter"=>"35 ...

  5. PHP in_array() 函数

    实例 在数组中搜索值 "Runoob" ,并输出一些文本: <?php $sites = array("Google", "Runoob&quo ...

  6. PHP xml_parser_free() 函数

    定义和用法 xml_parser_free() 函数释放 XML 解析器.高佣联盟 www.cgewang.com 如果成功,该函数则返回 TRUE.如果失败,则返回 FALSE. 语法 xml_pa ...

  7. PHP mysqli_refresh() 函数

    定义和用法 mysqli_refresh() 函数刷新表或缓存,或者重置复制服务器信息.高佣联盟 www.cgewang.com 语法 mysqli_refresh(connection,option ...

  8. AGC 043 C - Giant Graph SG函数 dp 贪心

    LINK:Giant Graph 神仙题目. 容易发现在图中选择某个点的贡献为\(10^{18\cdot(x+y+z)}\) 这等价于多选一个点多大一点就多乘了一个\(10^{18}\) 所以显然是贪 ...

  9. luogu P4887 模板 莫队二次离线 莫队 离线

    LINK:模板莫队二次离线 很早以前学的知识点 不过 很久了忘了. 考虑暴力 :每次莫队更新的时候 尝试更新一个点到一个区间的答案 可以枚举二进制下位数为k的数字 看一下区间内的这种数字有多少个. 不 ...

  10. 卷积神经网络 part1

    [任务一]视频学习心得及问题总结 根据下面三个视频的学习内容,写一个总结,最后列出没有学明白的问题. [任务二]代码练习 在谷歌 Colab 上完成代码练习,关键步骤截图,并附一些自己的想法和解读. ...