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

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

有两组进程,一组是生产者进程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日)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值