首页 > 其他分享 >408OS_PV操作大题总结

408OS_PV操作大题总结

时间:2024-09-24 19:24:24浏览次数:1  
标签:总结 PV 408OS 写者 互斥 while mutex semaphore 进程

咸鱼今年压了读者写者问题,前几年没考过。

死锁的四个条件是:

  • 禁止抢占(no preemption):系统资源不能被强制从一个线程中退出。
  • 持有和等待(hold and wait):一个线程在等待时持有并发资源。持有并发资源并还等待其它资源,也就是吃着碗里的望着锅里的。
  • 互斥(mutual exclusion):资源只能同时分配给一个线程,无法多个线程共享。
  • 循环等待(circular waiting):一系列线程互相持有其他进程所需要的资源。必须有一个循环依赖的关系。

死锁只有在四个条件同时满足时发生,预防死锁必须至少破坏其中一项。

生产者-消费者问题

特点:进程与进程之间是“ 生产资源一消耗资源的关系
六步做题

  1. 有几类进程?——每类进程对应一个函数
  2. 在每个函数内部,用自己的语言描述进程动作(只做一次-不加while 或者 不断重复-加while(1)
  3. 分析每一动作之前,是否需要P()操作
    P()必有V(),每写一个P(),就安排一个V()
  4. 所有 PV 写完后,才去定义信号量,定义完再确认每个信号量的初值是多少
  5. 注意连续出现多个P操作是否会导致死锁
  6. 检查题意

例题:
img

graph TD A[生产者A] --> F1[工厂F1] B[生产者B] --> F2[工厂F2] F1 --> C[组装工人] F2 --> C

分析:

  1. 几类进程?—— 2类:生产车间和装配车间

  2. 描述每类动作
    见下图绿蓝紫的框架部分。

  3. 安排PV操作
    分析每一动作之前,是否需要P()一个东西。
    以车间1为例:

  • 生产A零件之前不需要等待任何资源,所以生产A零件之前不需要P();
  • 把A零件放入F1之前需要确保F1有空位,因此需要P(empty_F1)
  • 什么地方会V(empty_F1)?——装配工人从F1取走
  • F2同理
  • 从F1取A,A可能不够,所以在此之前需要P(A),V(A)在把A放入F1之后
  • 从F2取B,同理
  • 组装不需要PV操作
  1. 定义信号量
Semaphore mutex1=1; //互斥访问货架F1
Semaphore mutex2=1; //互斥访问货架F2
Semaphore empty1=10; //货架F1上有几个空位
Semaphore full1=0; //货架F1上有几个A零件
Semaphore empty2=10; //货架F2上有几个空位
Semaphore full2=0; //货架F2上有几个B零件
  1. 注意连续出现多个P操作是否会发生死锁
  2. 检查题意

img

单纯的同步问题——前驱后继问题

可以按照生产者消费者问题的六步做题法来做。

哲学家进餐问题

一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子己在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。

img
特点: 只有一类进程,但需要占用多个资源才能运行
三种思路:

  1. 限制申请资源的顺序。如:规定单号哲学家先取左筷子,双号先取右筷子。(不建议使用)
  2. 限制并发进程数。如:规定同一时间只能有一个哲学家就餐(禁止并行)。(也不建议使用)
  3. 让进程一口气取得所有资源,再开始运行。如:哲学家只有能够取得两个筷子的时候才会就餐。
// 代码来自https://www.cnblogs.com/lordtianqiyi/p/17677155.html
semaphore mutex = 1;    // 互斥拿筷子
semaphore chopstick[5] = {1,1,1,1,1};
void philosopher(int i){
    while(true)
    {
        /*当哲学家饥饿时,总是先拿左边的筷子,再拿右边的筷子*/
        P(mutex)
        P(chopstick[i]);
        P(chopstick[(i+1)%5]);
        V(mutex)

        // 吃饭
    
        /*当哲学家进餐完成后,总是先放下左边的筷子,再放下右边的筷子*/
        V(chopstick[i]);
        V(chopstick[(i+1)%5]);
    }
}

为防止同时拿到左筷子而死锁的情况,这里在拿筷子时加了一个互斥信号量mutex。拿筷子时互斥,这样就不会出现同时拿到左筷子的情况。但是效率较低。
更好的做法是把筷子变成int型变量

// 万能模板
semaphore mutex = 1;    // 互斥拿筷子
int chopstick[5] = {1,1,1,1,1};
void philosopher(int i){
    while(true)
    // 当哲学家左右筷子不全在时,跳转回while,循环
    {
        P(lock);
        if (chopstick[i] && chopstick[(i+1)%5] ){
            // 条件是所有资源都足够
            -- chopstick[i];
            -- chopstick[(i+1)%5];
            // 所有资源int值--
            V(mutex);
            break;
        }else{
            V(mutex); // 跳回while
        }
        // 吃饭 

        P(lock); // 一口气归还所有资源
        ++ chopstick[i];
        ++ chopstick[(i+1)%5]; 
        V(lock);
    }
}

例题:2019真题
img
分析:

  1. 姑且认为只有一类进程——哲学家

philosopher(){
    while(1){
        get left chopstick;
        get right chopstick;
        get bowl;
        eat;
    }
}

理发师问题

是服务-被服务的关系
img
分析:

  • 有几类进程?—— 2类:理发师和顾客
  • 没有顾客时理发师可不可以睡觉?可以的话则阻塞;不可以(2011.45)则循环忙等
  • 座位是否有限?座位不够顾客跑路,座位够顾客等待
  • 如何用代码表示取号和叫号
int waiting = 0; // 已取号但还没被服务的人数
semaphore mutex_wait = 1; // 互斥访问waiting
semaphore Service = 0; // 服务信号量
semaphore customer = 0; // 顾客信号量,用于理发师睡觉

// 顾客
customer_i(){
    while(1){
        
        get number;
        P(mutex_wait);
        waiting++;
        V(mutex_wait);
        // 被叫号之后才能被服务
        // V(customer); // 叫醒理发师
        等待被叫号;
        P(Service); // 被叫号对应一次服务
        // cut hair;
    }
}

// 理发师
server_j(){
    while(1){ // 如果不睡觉,理发师需要循环工作
        P(mutex_wait);
        if(waiting>0){ // 有人在等待
            call number;
            waiting--;
            V(mutex_wait);
            V(Service);
            // cut hair;
        }else{
            V(mutex_wait); 
            // P(customer) //睡觉
        }
    }
}

读者写者问题

读者和写着两组并发进程共享一个文件,要求:多个读者可以同时读文件;只允许一个写者写文件;任一写者在完成写操作之前不允许其他人工作;写者写之前,应该让已有的读写者都退出。
分析:本质就是连续多个同类进程访问同一个临界资源的问题
2类进程:读者和写者。
其中有互斥关系的是:写者之间、写者与读者之间。
代码参考https://www.cnblogs.com/Mount256/p/16804609.html
这位老哥写得挺好的,我就没必要再改了,就加了点注释。

读优先

int count = 0; // 读者数量
semaphore busy = 1; // “读文件”和“写文件”的互斥锁
semaphore mutex = 1; // 变量 count 的互斥锁

Reader(){ // 读者进程
    while(1){
        P(mutex); // 给count上锁
        count++; 
        if (count == 1){ // 有人读时加锁,后面人继续读不用再加锁了
            P(busy); // 有读者时因互斥关系不能写
        }
        V(mutex);
        
        // 读文件; 
        
        P(mutex);
        count--; 
        if (count == 0){ // 没有读者时去锁
            V(busy);
        }
        V(mutex);
    }
}

Writer(){ // 写者进程
    while(1){
        P(busy);
        // 写文件;
        V(busy);
    }
}

读优先缺点是读进程会抢占资源到站写者饥饿。

读写公平

int count = 0;
semaphore queue = 1; // 实现“读写公平”的互斥锁,可以视为一个队列
semaphore busy = 1; // “读文件”和“写文件”的互斥锁
semaphore mutex = 1; // 变量 count 的互斥锁

Reader(){ // 读者进程
    while(1){
        P(queue); // 在无写进程请求时不需要进入队列
        P(mutex); // 该互斥量实际上是多余的,上面语句已经兼有互斥功能
        count++; 
        if (count == 1){ 
            P(busy);
        }
        V(mutex); // 该互斥量实际上是多余的,下面语句已经兼有互斥功能
        V(queue); // 恢复对共享文件的访问,不然其他读者没法读了
        
        // 读文件; 
        
        P(mutex);
        count--; 
        if (count == 0){ 
            V(busy);
        }
        V(mutex);
    }
}

Writer(){ // 写者进程
    while(1){
        P(queue); // 在无其他写进程请求时不需要进入队列
        P(busy);
        // 写文件;
        V(busy);
        V(queue); // 恢复对共享文件的访问
    }
}

这样子有一个写者排入队列就会把后面的读者堵住,后面的读者形成不了抢占。
顺序为:之前的读者-写者-后来的读者

写优先

int ReaderCount = 0; // 读者数量
int WriterCount = 0; // 写者数量
semaphore Read = 1; // “读文件”的互斥锁
semaphore Write = 1; // “写文件”的互斥锁
semaphore ReaderMutex = 1; // 变量 ReaderCount 的互斥锁
semaphore WriterMutex = 1; // 变量 WriterCount 的互斥锁

Reader(){ // 读者进程
    while(1){
        P(Read); // 每个读进程都需要对 Read 加锁
        P(ReaderMutex); // 对 ReadCount 的互斥,实际上,上条语句已经兼有此功能,可以去掉
        ReaderCount++; 
        if (ReaderCount == 1){ // 如果是第一个读进程
            P(Write); // 则对写者上锁
        }
        V(ReaderMutex); // 对 ReadCount 的互斥,实际上,下条语句已经兼有此功能,可以去掉
        V(Read); // Read 解锁
        
        读文件; 
        
        P(ReaderMutex); // 对 ReadCount 的互斥
        ReaderCount--; 
        if (ReaderCount == 0){ // 如果是最后一个读进程
            V(Write); // 则对写者解锁
        }
        V(ReaderMutex); // 对 ReadCount 的互斥
    }
}

Writer(){ // 写者进程
    while(1){
        P(WriterMutex); // 对 WriterCount 的互斥
        WriterCount++; 
        if (WriterCount == 1){ // 如果是第一个写进程
            P(Read); // 则对读者上锁
        }
        V(WriterMutex); // 对 WriterCount 的互斥
        
        P(Write); // Write 加锁
        写文件; 
        V(Write); // Write 解锁
        
        P(WriterMutex); // 对 WriterCount 的互斥
        WriterCount--; 
        if (WriterCount == 0){ // 如果是最后一个写进程
            V(Read); // 则对读者解锁
        }
        V(WriterMutex); // 对 WriterCount 的互斥
    }
}

标签:总结,PV,408OS,写者,互斥,while,mutex,semaphore,进程
From: https://www.cnblogs.com/CauchyPt/p/18429835

相关文章

  • 超详细的系列总结!大模型岗面试题(含答案)来了!(大语音模型基础篇二)
    前言大模型应该是目前当之无愧的最有影响力的AI技术,它正在革新各个行业,包括自然语言处理、机器翻译、内容创作和客户服务等,正成为未来商业环境的重要组成部分。截至目前大模型已超过200个,在大模型纵横的时代,不仅大模型技术越来越卷,就连大模型相关岗位和面试也开始越来越卷......
  • JVM虚拟机总结
        读了周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践》第三版,总结一下里面的知识点。一方面是知识储备更多一些,另外是也为接下来的面试准备一下。    全书分为13个章节,共5部分内容。我着重是看了jvm的内管管理、垃圾收集与内存分配策略、虚拟机故障......
  • Mysql知识库【总结】
    MySQL是一种关系型数据库管理系统(RDBMS),其底层原理可以简单概括为以下几个方面:-存储引擎:MySQL支持多种存储引擎,如MyISAM、InnoDB、Memory等。每种存储引擎的实现方式不同,它们各自的特点和使用场景也不同。例如,MyISAM存储引擎适合于读多写少的场景,而InnoDB存储引擎则适合于......
  • 9.23考试总结
    T1简单签到题,考虑一个点从开头移到结尾会减去小于它的数加上大于它的数。所以\(O(nlogn)\)求逆序对,然后\(O(1)\)计算一个数移到最后的答案。#include<bits/stdc++.h>usingnamespacestd;constintN=1e6+10;#definelllonglongintn,a[N],sum[N],sh[N];llans,jg;......
  • 1.21之前都是pvc一旦创建立马绑定pv 之后通过参数进行了解耦
    从Kubernetesv1.21开始,PVC支持volumeBindingMode字段,它可以设置为Immediate(立即绑定)或WaitForFirstConsumer(等待第一个消费者)。Immediate模式下,Kubernetes会立即尝试为PVC绑定PV。WaitForFirstConsumer模式下,Kubernetes会等待直到一个Pod引用了该PVC后再为其绑定PV。......
  • k8s pv 和 pvc
    要退出全屏模式,请按EscAccessModes(访问模式):AccessModes是用来对PV进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载ReadOnlyMany(ROX):只读权限,可以被多个节点挂载ReadWriteMany(RWX......
  • 2024/09/23 模拟赛总结
    rk3,\(0+100+30+5=135\)#A.依依寺唐氏分类讨论,赛事写了个记搜爆0了因为\(0\)不会改变取得数的和,所以\(a\)可以改为\(a\bmod2\)。接下来分类讨论假设先手取\(1\),那么后手取\(2\)直接输,则一定先取\(1\),接下来先手取\(1\)又输,只能取\(2\),然后就会循环后手\(1\)......
  • 总结
    集合考虑枚举子集和,统计有多少个子集的和为当前枚举的子集和,然后我们记个结论:\(x^y=x^{y\mod(p-1)}\),然后就过了P3488一眼二分图(网络流启动),但是考虑到图很大,所以我们考虑直接判断是否是二分图,考虑一个区间,如果总数比这个区间所能承载的人都要大,那么肯定会寄,所以用线段树维......
  • AI大模型大厂面经——LoRA面试题最全总结
    前言大家的显卡都比较吃紧,LoRA家族越来越壮大,基于LoRA出现了各种各样的改进,最近比较火的一个改进版是dora,听大家反馈口碑也不错。基于PEFT的话用409024G显存也可以进行大模型的微调,所以LoRA家族这块还是很有研究和实际落地的潜力。LoRA整个系列分为两个部分:1、LoRA总述2、LoRA家族......
  • NOIP2024集训 Day37 总结
    前言今天的题目也是比较快速的做完了。所以先来总结一下。今天是计数专题,组合数居多。以前做过的题目这里就稍稍略过了。MergeTriplets观察到对于能够得到的最终的排列\(p\),对于其中的一个数\(p_i\),不可能做到\(p_i>\max_{j=i+1}^{i+3}p_j\)。感觉是比较显然的,这里就不......