操作系统经典问题

上课老师给我们讲了一些操作系统的经典题目,一天过去了,今天我就来尝试一下用自己的理解来解释这些题目。

信号量解题的关键

设置信号量的步骤

  1. 信号量的设置
  2. 给信号量赋初值(常用的互斥和同步信号量值的大小)
  3. P、V操作安排的位置(其中,P的顺序不能颠倒,V的顺序任意)(一般情况下是这样的)

需要注意

  1. 公用信号量:互斥时使用的信号量(二元信号量):它仅允许取值为0与1,用作互斥。它联系着一组共行进程,初值为1,每个进程均可对之施加P、V操作。
  2. 私用信号量(一般信号量(资源信号量)):它联系着一组共行进程,但其初值为0,或为某个正整数n,表示资源的数目,主要用于进程同步。只允许拥有它的进程对之施加P操作。

生产者-消费者问题(进程同步)

生产者是先制造产品,在存放到公共仓库,随后由消费者从仓库中取出这些产品进行消费。尽管生产者和消费者是以异步的方法运行的,但他们之间必须保持同步,即消费者不能到空的仓库去取产品消费,生产者也不能讲产品存放到已满的仓库中。此外仓库每次也只允许一个人进出。

其实计算机中很多并发进程之间的同步关系都能抽象成生产者-消费者模型,比如说计算进程和打印进程。

下面给出解题的思路

  1. 想接收数据时,有界缓冲区中至少有一个单元是满的

  2. 生产者想发送数据时,有界缓冲区中至少有一个单元是空的

由于缓冲区是临界资源,所以生产者和消费者之间必须互斥的访问临界资源

首先我们需要设置一个公有信号量为mutex来表示仓库这个公有资源的状态,初值为1;其次我们需要给出一个消费者的私有信号量full来表示仓库产品的个数,初值为0;生产者私用信号量avail来表示仓库的空位置的个数,初值为n

伪代码

Deposit(data):
	begin
        P(avail)		// 检查仓库中是否有空位  执行后n - 1
        P(mutex)		// 检查仓库是否可用  执行后 mutex = 0
        将产品送入仓库
        V(full)			// 将产品个数加1
        V(mutex)		// 释放仓库这个资源
	end
      
Remove(data):
	begin
        P(full)			// 检查仓库中是否有产品
        P(mutex)		// 检查仓库是否可以使用
        从仓库中取出一个产品
        V(avail)		// 将空位置的个数加1
        V(mutex)		// 释放仓库这个资源
	end

其实这个伪代码是这样运行的,我们先可以假设生产者要先发送数据。当生产者想要将发送数据的时候,他就必须要先检查仓库是否是有空位也就是伪代码中的P(avail),当仓库有空位的时候,就让avail-1,表示仓库有空位,生产者可以存放产品。接着就会继续执行P(mutex)去占用的公共仓库资源,假如占用的话就会让原来的mutex = 1变成mutex = 0,即表示公共资源被申请占用,假如这个时候下面的消费者就算检测到仓库中有产品,但是当执行下面Remove(data)P(mutex)的时候,就会让mutex = 0变成mutex = -1这样消费者取产品这个动作就会变成阻塞状态,这样就两者就无法对仓库进行操作,即每次只能进入一个人。这里我们需要提出的一个问题就是P(avail)P(mutex)能否顺序调换呢?答案当然是不能的,假设一种情况,当我们的仓库没有空位的时候,而Deposit(data)中的P(mutex)先执行,导致仓库被加锁,消费者无法从中取出产品,这就导致系统死锁,消费者无法取出商品,而生产者一直在P(avail)。所以每个操作中P原语是不能调换的,但是V原语是可以调换的。

读者和写者问题(进程同步)

一个文件可能被多个进程共享,为了保证读写的正确性和文件的一致性,系统要求,当有读者进程读文件时,不允许任何写者进程写,但允许多读者同时读;当有写者进程写时,不允许任何其它写者进程写,也不允许任何读者进行读。

互斥
互斥
互斥
互斥

为了解决读者和写者问题,需要设置两个信号量

  1. 互斥信号量rmutex用于读者互斥地访问共享变量readcont,而readcont是记录有多少读者正在读;
  2. 互斥信号量wmutex用于实现读写互斥

伪代码

struct semapore rmutex, wmutex = 1, 1;
int readcount = 0;
cobegin
void readeri(void)(i = 1 , 2, …k) {
	while(true){            
		p(rmutex);			
		if readcount = 0 then p(wmutex);                
		readcount:=readcount + 1;    
		v(rmutex);                
		读文件;
		p(rmutex); 
		readcount:=readcount - 1;
		if readcount = 0 then v(wmutex);
		v(rmutex);
	}
}
void writerj(void)(j = 1, 2, …m) {
	while(true){
		p(wmutex);
		写文件;
		v(wmutex);
	}
}
coend

现在我们先假设有写者在写时,而没有读者进行读操作,就会去执行writerj()中的P(wmutex)申请进入共享数据区,而后wmutex变为0,然后开始进入了一系列的写操作,我们可以假设这个时候有读者想要进行读操作,首先是当然是锁readcountP(rmutex),然后需要判断现在的读者数量,为0,进行P(wmutex)操作,这个时候wmutex变为-1,导致写进程阻塞,完成了读写的互斥操作。那我们继续设想假如说我的写操作完成了,并且释放了wmutexwmutex重新变为了0,这样读操作就成功拿到了共享资源的访问权限,就可以readcount + 1,假设当我们的读者读文件完成之后,我就会重新申请rmutex申请访问共享变量readcount。然后就算在有读者来访问时,一定要等前一个读者释放rmutex才能继续操作。同样的,如果读者数量变为0的时候,我们就需要释放wmutex,即wmutex + 1 = 0,这样写者就继续写操作。

图书馆阅览室问题(进程同步)

假定阅览室最多可同时容纳100个人阅读,读者进入时,必须在阅览室门口的一个登记表上登记,内容包括姓名、座号等,离开时要撤掉登记内容。

解题思路

读者有任意多个,但进入阅览室阅读最多为100人,为此可设一个信号量s,代表空座位的数目;另登记表为临界资源,需设一个用于互斥的信号量mutex,防止2个及以上的读者进程同时对此表访问。对于每个读者的动作包括进入、阅读、离开。

伪代码

struct semaphore s, mutex = 100, 1;
cobegin 
void readeri(void) (i = 1, 2, …, k) { 
	while(TRUE){
		P(s);
		P(mutex);
		查登记表,置某座位为占用;
		V(mutex);
		......
		reading;
		P(mutex);
		登记表,置某座位为空;
		V(mutex);
		V(s);
	}
}
coend

首先要说明为什么这是并发代码,明明只有一个部分,因为这个阅览室是可以进行共同访问的,所以也可以产生并发。假设我们有读书人想要进入阅览室,那么就会执行P(s)让这个s的值-1,然后就开始申请占用登记表的资源P(mutex),这样mutex就变为了0,就占用了登记表的资源,这样其他读书人先要再次访问登记表的时候就会阻塞,只能等待前一个读书人是释放mutex,然后就是读书完成,退还的时候,还是同样的道理。

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值