操作系统理论 第二章(进程的描述与控制)—第六节(经典进程的同步问题)

写在前面:

  1. 本系列笔记主要以《计算机操作系统(汤小丹…)》为参考,大部分内容出于此书,笔者的工作主要是挑其重点展示,另外配合下方视频链接的教程展开思路,在笔记中一些比较难懂的地方加以自己的一点点理解(重点基本都会有标注,没有任何标注的难懂文字应该是笔者因为强迫症而加进来的,可选择性地忽略)。
  2. 视频链接:操作系统(汤小丹等第四版)_哔哩哔哩_bilibili

一、生产者-消费者问题

1、利用记录型信号量解决生产者-消费者问题

(1)假定在生产者和消费者之间的公用缓冲池中具有n个缓冲区,这时可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用,利用信号量empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入缓冲池;只要缓冲池未空,消费者便可从缓冲池中取走一个消息。

int in = 0, out = 0;

item buffer[n];

semaphore mutex = 1, empty = n, full = 0;

void producer()

{

        do

        {

                producer an item nextp;  //生产产品

                …

                wait(empty);  //缓冲池容量-1

                wait(mutex);  //占用缓冲池(进入临界区)

                buffer[in] = nextp;  //缓冲池装货

                in = (in+1)%n;      //修改生产者进程指向缓冲池的指针

                signal(mutex);  //释放缓冲池(离开临界区)

                signal(full);    //缓冲池货物量+1

        }while(TRUE);

}

void consumer()

{

        do

        {

                wait(full);    //缓冲池货物量-1

                wait(mutex);  //占用缓冲池(进入临界区)

                nextc = buffer[out];  //缓冲池出货

                out = (out+1)%n;    //修改消费者进程指向缓冲池的指针

                signal(mutex);  //释放缓冲池(离开临界区)

                signal(empty);  //缓冲池容量+1

                consumer the item in nextc;  //消费产品

                …

        }while(TRUE);

}

(2)注意事项:

在每个程序中用于实现互斥的wait(mutex)和signal(mutex)必须成对地出现

对资源信号量empty和full的wait和signal操作,同样需要成对地出现,但它们分别处于不同的程序中。例如,wait(empty)在计算(生产者)进程中,而signal(empty)则在打印(消费者)进程中,计算进程若因执行wait(empty)而阻塞,则以后将由打印进程将它唤醒。

在每个程序中的多个wait操作顺序不能颠倒。应先执行对资源信号量的wait操作,然后再执行对互斥信号量的wait 操作,否则可能引起进程死锁。

2、利用AND信号量解决生产者-消费者问题

        对于生产者-消费者问题,也可利用AND信号量来解决,即用Swait(empty, mutex)来代替wait(empty)和wait(mutex),用Ssignal(mutex, full)来代替signal(mutex)和signal(full);用Swait(full, mutex)代替wait(full)和wait(mutex),以及用Ssignal(mutex, empty)代替Signal(mutex)和Signal(empty)。

int in = 0, out = 0;

item buffer[n];

semaphore mutex = 1, empty = n, full = 0;

void producer()

{

        do

        {

                producer an item nextp;  //生产产品

                …

                Swait(empty, mutex);  //缓冲池容量-1,占用缓冲池(进入临界区)

                buffer[in] = nextp;  //缓冲池装货

                in = (in+1)%n;      //修改生产者进程指向缓冲池的指针

                Ssignal(mutex, full);  //释放缓冲池(离开临界区),缓冲池货物量+1

        }while(TRUE);

}

void consumer()

{

        do

        {

                Swait(full, mutex);    //缓冲池货物量-1,占用缓冲池(进入临界区)

                nextc = buffer[out];  //缓冲池出货

                out = (out+1)%n;    //修改消费者进程指向缓冲池的指针

                Ssignal(mutex, empty);  //释放缓冲池(离开临界区),缓冲池容量+1

                consumer the item in nextc;  //消费产品

                …

        }while(TRUE);

}

二、哲学家进餐问题

1、问题描述

        有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐;进餐毕,放下筷子继续思考。

        根据以上规则可以得出,相邻两位哲学家不能同时进餐,最多只能有两人同时进餐

2、用记录型信号量解决哲学家进餐问题

(1)放在桌子上的筷子是临界资源,在一段时间内只允许一个哲学家使用。为实现对筷子的互斥使用,用一个信号量表示一只筷子,五个信号量构成信号量数组。

semaphore chopstick[5] = {1, 1, 1, 1, 1};  //筷子数组

void eater_i()  //第i位哲学家的活动

{

        do

        {

                wait(chopstick[i]);        //拿起左边的筷子

                wait(chopstick[(i+1)%5]);  //拿起右边的筷子

                …  //只要少一根筷子都吃不上饭,要将自己阻塞起来等筷子

                //吃饭

                …

                signal(chopstick[i]);        //放下左边的筷子

                signal(chopstick[(i+1)%5]);  //放下右边的筷子

                …

                //思考

                …

        }while(TRUE)

}

(2)假如五位哲学家同时饥饿而各自拿起左边的筷子时,就会使五个信号量chopstick均为0,当他们再试图去拿右边的筷子时,都将因无筷子可拿而无限期地等待,从而进入死锁。对于这样的死锁问题,可采取以下几种解决方法:

至多只允许有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐,并在用毕时能释放出他用过的两只筷子,从而使更多的哲学家能够进餐。

semaphore chopstick[5] = {1, 1, 1, 1, 1};  //筷子数组

semaphore count = 4;             //最多允许4位哲学家拿左边的筷子

void eater_i()  //第i位哲学家的活动

{

        do

        {

                wait(count);             //“举手”示意要拿起左边的筷子

                wait(chopstick[i]);        //拿起左边的筷子

                wait(chopstick[(i+1)%5]);  //拿起右边的筷子

                …  //只要少一根筷子都吃不上饭,要将自己阻塞起来等筷子

                //吃饭

                …

                signal(chopstick[i]);        //放下左边的筷子

                signal(chopstick[(i+1)%5]);  //放下右边的筷子

                …

                //思考

                …

        }while(TRUE)

}

仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐,否则一根筷子都不能拿。

semaphore chopstick[5] = {1, 1, 1, 1, 1};  //筷子数组

void eater_i()  //第i位哲学家的活动

{

        do

        {

                Swait(chopstick[i] ,chopstick[(i+1)%5]);    //同时拿起两边的筷子

                …  //只要少一根筷子都吃不上饭,要将自己阻塞起来等筷子

                //吃饭

                …

                Ssignal(chopstick[i], chopstick[(i+1)%5]);   //同时放下两边的筷子

                …

                //思考

                …

        }while(TRUE)

}

规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子,而偶数号哲学家则相反。按此规定,将是1、2号哲学家竞争1号筷子,3、4号哲学家竞争3号筷子,即五位哲学家都先竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一位哲学家能获得两只筷子而进餐。

semaphore chopstick[5] = {1, 1, 1, 1, 1};  //筷子数组

void eater_i()  //第i位哲学家的活动

{

        do

        {

                if(i % 2 == 1)  //奇数号哲学家

                {

                        wait(chopstick[i]);        //拿起左边的筷子

                        wait(chopstick[(i+1)%5]);  //拿起右边的筷子

                }

                else         //偶数号哲学家

                {

                        wait(chopstick[(i+1)%5]);   //拿起右边的筷子

                        wait(chopstick[i]);         //拿起左边的筷子

                }

                …

                //吃饭

                …

                signal(chopstick[i]);        //放下左边的筷子

                signal(chopstick[(i+1)%5]);  //放下右边的筷子

                …

                //思考

                …

        }while(TRUE)

}

3、利用AND信号量解决哲学家进餐问题

        在哲学家进餐问题中,要求每个哲学家先获得两个临界资源(筷子)后方能进餐,这在本质上就是前面所介绍的AND同步问题(也就是上面解决死锁的第二种方法),故用AND信号量机制可获得最简洁的解法。

三、读者-写者问题

1、问题描述

        一个数据文件或记录可被多个进程共享,把只要求读该文件的进程称为“Reader进程”,其它进程则称为“Writer进程”。允许多个进程同时读一个共享对象,因为读操作不会使数据文件混乱;但不允许一个Writer进程和其它Reader进程或Writer进程同时访问共享对象,因为这种访问将会引起混乱。

        所谓“读者-写者问题”是指保证一个Writer 进程必须与其它进程互斥地访问共享对象的同步问题。

2、用记录型信号量解决读者-写者问题

        为实现Reader与Writer进程间在读或写时的互斥,设置一个互斥信号量wmutex,另外再设置一个整型变量readcount表示正在读的进程数目。

        ①只要有一个Reader进程在读,便不允许Writer进程去写,因此仅当readcount=0,表示尚无Reader进程在读时,Reader进程才需要执行wait(wmutex)操作,若wait(wmutex)操作成功,Reader进程便可去读,相应地,做readcount+1操作。

        ②仅当Reader进程在执行了readcount减1操作后其值为0时,才须执行signal(wmutex)操作,以便让Writer进程写操作。

        ③因为readcount是一个可被多个Reader进程访问的临界资源,因此也应该为它设置一个互斥信号量rmutex。

semaphore rmutex = 1, wmutex = 1;

void Reader()

{

        do

        {

                wait(rmutex);

                if(readcount == 0)  //如果该进程是第一个读文件的,要负责给写进程上锁

                wait(wmutex);  //如果文件正在被写进程访问,读进程将会自我阻塞

                readcount++;  //读者+1

                signal(rmutex);

                …

                perform read operation;   //读操作

                …

                wait(rmutex);

                readcount--;  //读者-1

                if(readcount == 0)  //如果该进程是最后一个读完文件的,要负责给写进程解锁

                signal(wmutex);

                signal(rmutex);

        }while(TRUE)

}

void Writer()

{

        do

        {

                wait(wmutex);   //检查是否被上锁(未上锁则上锁后开始读)

                perform write operation;  //写操作

                signal(wmutex);  //解锁,其它进程可以进行读写

        }while(TRUE)

}

3、利用AND信号量解决读者-写者问题

        增加一个限制,最多只允许R个读者同时读,为此又引入一个信号量L,并赋予其初值为RN,通过执行wait(L,1,1)操作来控制读者的数目。

        ①每当有一个读者进入时,就要先执行wait(L,1,1)操作,使L的值减1。

        ②当有RN个读者进入读后,便减为0,第RN+1个读者要进入读时,必然会因wait(L,1,1)操作失败而阻塞。

int RN;

Semaphore L = RN, mx = 1;          //mx用来标志文件是否有写进程访问

void Reader()

{

        do

        {

                Swait(L, 1, 1);            //文件被本进程读,允许读的读者-1

                Swait(mx, 1, 0);          //没有写进程访问文件则可以读

                …

                perform read operation;   //读操作

                …

                Ssignal(L,1);             //本进程读文件结束,允许读的读者+1

        }while(TRUE)

}

void Writer()

{

        do

        {

                Swait(mx, 1, 1; L, RN, 0);   //没有读进程和其它写进程访问文件,方可开始写

                perform write operation;  //写操作

                Ssignal(mx, 1);           //结束写

        }while(TRUE)

}

  • 49
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值