操作系统原理概述:经典的进程同步与互斥问题

有限缓冲区的生产者-消费者问题

有两组进程,一组是生产者进程P1,P2,…,Pn,另一组是消费者进程c1,c2,…,cm,它们共享一个公有的、固定大小的缓冲池,池中有n个缓冲区,一个缓冲区存放一个产品。生产者不断地制造出产品,并把它放入到指针in指定的缓冲区;而消费者不断地根据指针out指定的缓冲区把产品从缓冲区中取出来消费。要求这两组进程之间相互协调,使它们都能正确地完成自己的工作。

具有n个缓冲区的环型缓冲池,按顺时针生产和消费,in为生产者指针,生产完成一个产品,就将产品插入in指针指定的缓冲区,out为消费者指针,消费者从out指针指向的缓冲区中取出产品。

  • 缓冲池满的条件:(int + 1) mod n = out

  • 缓冲池空的条件:in = out

# 设置以下信号量:
#   mutex,初值为1,用于互斥,控制互斥访问缓冲池。
#   full,初值为0,表示当前缓冲池中已用缓冲区数,用于同步,即缓冲池中现有产品数,一个缓冲区放一个产品。
#   empty,初值为n,表示当前缓冲池中空缓冲区数,用于同步。

void producer()
{
    while(true){
        produce;                // 生产一个产品
        P(empty);               // 有无缓冲区
        P(mutex);               // 能否进入临界区            
        产品送往buffer(in);      // 将产品放入in下标指定的缓冲区 
        in = (in - 1) mod n;    // 生产者缓冲区下标+1
        V(mutex);               // 退出临界区
        V(full);                // 缓冲池中产品数+1
    }
}

void consumer()
{
    while(true){
        P(full);                 // 有无产品消费
        P(mutex);                // 能否进入临界区buffer(out)中取出产品;  // 从out下标指定的缓冲区取出产品
        out = (out - 1) mod n;   // 消费者缓冲区下标+1
        V(mutex);                // 退出临界区
        V(empty);                // 空缓冲区数+1
        consume;                 // 消费取出的产品
    }
}

void main()
{
    semaphore empty=n, full=0, mutex=1;
    cobegin
        producer();
        consumer();
    coend
}
哲学家就餐问题

5位哲学家围坐在一张圆桌旁,每位哲学家面前都摆放着一盘面条,但面条非常滑,所以如果想要吃的话,必须同时使用两根筷子。在任意两个相邻的盘子之间都有一根筷子。每一位哲学家的动作只有两种:进餐和思考问题。当一位哲学家感到饥饿时,他会试图去获得位于他左手边和右手边的那两根筷子,每次只能取一根,且先后顺序无所谓,两根筷子都拿到后,才能开始进餐。在吃完以后,他需要把两根筷子放回原处,然后继续思考。5位哲学家和5根筷子都进行了编号,从0到4。依此类推,对于第i位哲学家,他的左手边是第i根筷子,右手边是第i+1根筷子。当然,最后一位哲学家除外,他左手边是4号筷子,而右手边是0号筷子。

解决方案1:每次最多只允许四个哲学家并发,同时拿筷子,这样肯定有一个哲学家能吃上面条,吃完后放回筷子,其他哲学家就能吃上面条。因此,可增设一个可同时就餐的人数信号量room,初值为4,表示每次可同时就餐人数最多4人。

# define N 5

void philosopher(int i)
{
    do{
        think();
        P(room);
        P(chopstick[i]);
        P(chopstick[(i+1)%N]);
        eat();
        V(chopstick[(i+1)%N]);
        V(chopstick[i]);
        V(room);
    }while(true);
}

void main()
{
    semaphore chopstick[0...4] = {1, 1, 1, 1, 1};
    semaphore room = 4;     // 设置可同时就餐的人数信号量room,初值为4
    int i;
    cobegin
        philosopher(0);
        philosopher(1);
        philosopher(2);
        philosopher(3);
        philosopher(4);
    coend
}

解决方案2:先判断哲学家的序号是奇数还是偶数,如是奇数,则先拿左边的筷子再拿右边的筷子;如果是偶数,则先拿右边的筷子再拿左边的筷子。

# define N 5

void philosopher(int i)
{
    do{
        think();
        if(i mod 2 == 0)
        {
            P(chopstick[i]);
            P(chopstick[(i+1)%N]);
            eat();
            V(chopstick[i]);
            V(chopstick[(i+1)%N]);
        }
        else
        {
            P(chopstick[(i+1)%N]);
            P(chopstick[i]);
            eat();
            V(chopstick[(i+1)%N]);
            V(chopstick[i]);
        }
    }while(true);
}
读者-写者问题

在一个航空订票系统当中,有很多个竞争的进程想要访问(读或写)系统的数据集。访问的规则是:在任何时候,可以运行多个进程同时来读,但如果有一个进程想要修改该数据集,那么在此期间,其他任何进程都不能访问,包括读者和写者。从读者和写者对数据集的访问过程来看,在任何时候,“写者”最多只允许一个,而“读者”可以有多个。具体来说:“读-写”是互斥的、“写-写”是互斥的、“读-读”是允许的。

读者优先:只要有读进程陆续到来,它们一来就被允许进入,而写进程将一直被挂起直到没有一个读进程为止。

# 一个公共变量:rc,记录读进程的个数
# 两个信号量:mutex,保护对rc的访问初值为1、db控制写进程对数据集的访问,初值为1。

semaphore mutex = 1;    // 保护对rc的访问
semaphore db = 1;       // 控制对数据集的访问
int rc = 0;             // 读数据集的读进程个数

void reader()           // 读者进程
{
    while(true)
    {
        P(mutex);       // 互斥对rc的访问
        rc = rc + 1;    // 增加一个读进程数
        if(rc == 1)     // 当第一个读进程读数据集时,阻止写进程写。
            P(db);  
        V(mutex);       // 恢复对rc的访问
        读数据集;
        P(mutex);       // 互斥对rc的访问
        rc = rc - 1;    // 读者减少一个
        if(rc == 0)     // 当最后一个读进程读完数据集时,允许写进程写
            V(db);
        V(mutex);       // 恢复对rc的访问
    }
}

void writer()           // 写者进程
{
    while(true)
    {
        P(db);          // 排斥访问数据集
        写数据库;
        V(db);          // 恢复访问数据集
    }
}

void main()
{
    cobegin             // 两类进程并发执行
        reader();
        writer();
    coend
}

写者优先:当有读进程正在读数据集时,有写进程请求访问,这时应禁止后续读进程的读请求,等到已在读数据集的读进程执行完毕则立即让写进程执行,只有在无写进程执行的情况下才允许读进程再次投入运行。

semaphore mutex = 1;    // 保护对rc的访问
semaphore db = 1;       // 控制对数据集的访问
int rc = 0;             // 读数据集的读进程个数

semaphore W = 1;        // 提高写进程优先级

void reader()           // 读者进程
{
    while(true)
    {
        P(W);           // 在无写进程请求时进入
        P(mutex);       // 互斥对rc的访问
        rc = rc + 1;    // 增加一个读进程数
        if(rc == 1)     // 当第一个读进程读数据集时,阻止写进程写。
            P(db);  
        V(mutex);       // 恢复对rc的访问
        V(W);           // 恢复对数据集的访问
        读数据集;
        P(mutex);       // 互斥对rc的访问
        rc = rc - 1;    // 读者减少一个
        if(rc == 0)     // 当最后一个读进程读完数据集时,允许写进程写
            V(db);
        V(mutex);       // 恢复对rc的访问
    }
}

void writer()           // 写者进程
{
    while(true)
    {
        P(W);           // 在无写进程请求时进入
        P(db);          // 排斥访问数据集
        写数据库;
        V(db);          // 恢复访问数据集
        V(W);           // 恢复对数据集的访问
    }
}

(最近更新:2019年09月18日)

实验题目: 生产者消费者(综合性实验) 实验环境: C语言编译器 实验内容: ① 由用户指定要产生的进程及其类别,存入进入就绪队列。    ② 调度程序从就绪队列中提取一个就绪进程运行。如果申请的资源被阻塞则进入相应的等待队列,调度程序调度就绪队列中的下一个进程进程运行结束时,会检查对应的等待队列,激活队列中的进程进入就绪队列。运行结束的进程进入over链表。重复这一过程直至就绪队列为空。    ③ 程序询问是否要继续?如果要转直①开始执行,否则退出程序。 实验目的: 通过实验模拟生产者消费者之间的关系,了解并掌握他们之间的关系及其原理。由此增加对进程同步问题的了解。 实验要求: 每个进程一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程类型标号、进程系统号、进程状态、进程产品(字符)、进程链指针等等。 系统开辟了一个缓冲区,大小由buffersize指定。 程序中有三个链队列,一个链表。一个就绪队列(ready),两个等待队列:生产者等待队列(producer);消费者队列(consumer)。一个链表(over),用于收集已经运行结束的进程 本程序通过函数模拟信号量的操作。 参考书目: 1)徐甲同等编,计算机操作系统教程,西安电子科技大学出版社 2)Andrew S. Tanenbaum著,陈向群,马红兵译. 现代操作系统(第2版). 机械工业出版社 3)Abranham Silberschatz, Peter Baer Galvin, Greg Gagne著. 郑扣根译. 操作系统概念(第2版). 高等教育出版社 4)张尧学编著. 计算机操作系统教程(第2版)习题解答与实验指导. 清华大学出版社 实验报告要求: (1) 每位同学交一份电子版本的实验报告,上传到202.204.125.21服务器中。 (2) 文件名格式为班级、学号加上个人姓名,例如: 电子04-1-040824101**.doc   表示电子04-1班学号为040824101号的**同学的实验报告。 (3) 实验报告内容的开始处要列出实验的目的,实验环境、实验内容等的说明,报告中要附上程序代码,并对实验过程进行说明。 基本数据结构: PCB* readyhead=NULL, * readytail=NULL; // 就绪队列 PCB* consumerhead=NULL, * consumertail=NULL; // 消费者队列 PCB* producerhead=NULL, * producertail=NULL; // 生产者队列 over=(PCB*)malloc(sizeof(PCB)); // over链表 int productnum=0; //产品数量 int full=0, empty=buffersize; // semaphore char buffer[buffersize]; // 缓冲区 int bufferpoint=0; // 缓冲区指针 struct pcb { /* 定义进程控制块PCB */ int flag; // flag=1 denote producer; flag=2 denote consumer; int numlabel; char product; char state; struct pcb * processlink; …… }; processproc( )--- 给PCB分配内存。产生相应的的进程:输入1为生产者进程;输入2为消费者进程,并把这些进程放入就绪队列中。 waitempty( )--- 如果缓冲区满,该进程进入生产者等待队列;linkqueue(exe,&producertail); // 把就绪队列里的进程放入生产者队列的尾部 void signalempty() bool waitfull() void signalfull() void producerrun() void comsuerrun() void main() { processproc(); element=hasElement(readyhead); while(element){ exe=getq(readyhead,&readytail); printf("进程%d申请运行,它是一个",exe->numlabel); exe->flag==1? printf("生产者\n"):printf("消费者\n"); if(exe->flag==1) producerrun();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值