概述
生产者消费者模型
生产者和消费者问题是计算机同步互斥的经典问题,其意思就是生产者把生产出来的产品放在仓库里,消费者把产品从仓库里取出来。仓库属于临界区,生产者和消费者一次只能一个进入临界区中。两个进程之间就有一个同步互斥问题,下面我将对该问题进行详细介绍。
什么是PV操作
PV操作是由P操作原语和V操作原语组成(原语是不可能中断的过程),操作对象是信号量。具体的:
P(S):① 将信号量S的值减1,即S=S-1;② 如果S>=0,则该进程继续执行;否则进程进入等待队列,置为等待状态。
V(S):① 将信号量S的值加1,即S=S+1;② 如果S>0,则该进程继续执行;否则释放等待队列中第一个等待信号量的进程。(因为将信号量加1后仍然不大于0,则表示等待队列中有阻塞的进程。)
#define N 100 //缓冲区大小
typedef int semaphore; //信号量是一种特殊的整形数据
semaphore mutex=1; //控制对临界区的访问
semaphore empty=N; //表示缓冲中空槽的数目
semaphore full=0; //表示缓冲中满槽的数目
void produce()
{
int item;
while(true)
{
item=produce_item(); //产生一个数据
P(&empty); //将空槽的数目减一
P(&mutex); //进入临界区
insert_item(item); //将产生的数据插入临界区
V(&mutex); //离开临界区
V(&full); //将满槽的数目加1
}
}
void consumer()
{
int item;
while(true)
{
P(&full); //将满槽的数目减一
P(&mutex); //进入临界区
item=remove_item(); //从缓冲区中取出一个数据
V(&mutex); //离开临界区
V(&empty); //将空槽数目加一
consumer_item(item); //处理数据
}
}
哲学家就餐问题
转载于:哲学家就餐问题
有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。
在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕,放下筷子又继续思考。
哲学家进餐问题可看作是并发进程并发执行时处理共享资源的一个有代表性的问题。
semphore chopstick[5]={1,1,1,1,1}
void philosopher(int i) //哲学家编号,从0-4
{
while (true)
{
think(); //哲学家在思考
wait(chopstick[i]); //拿起左边筷子
wait(chopstick[(i + 1) % 5]); //拿起右边筷子
eat(); //吃饭
signal(chopstick[i]); //放下左边筷子
signal(chopstick[(i + 1) % 5]); //放下右边筷子
}
}
此算法可以保证不会有相邻的两位哲学家同时进餐。
若五位哲学家同时饥饿而各自拿起了左边的筷子,这使五个信号量 chopstick 均为 0,当他们试图去拿起右边的筷子时,都将因无筷子而无限期地等待下去,即可能会引起死锁。
哲学家进餐问题的改进解法
- 方法一:至多只允许四位哲学家同时去拿左筷子,最终能保证至少有一位哲学家能进餐,并在用完后释放两只筷子供他人使用。
- 方法二:仅当哲学家的左右手筷子都拿起时才允许进餐。
- 方法三:规定奇数号哲学家先拿左筷子再拿右筷子,而偶数号哲学家相反。
方法一
至多只允许四位哲学家同时去拿左筷子,最终能保证至少有一位哲学家能进餐,并在用完后释放两只筷子供他人使用。
设置一个初值为 4 的信号量 r,只允许 4 个哲学家同时去拿左筷子,这样就能保证至少有一个哲学家可以就餐,不会出现饿死和死锁的现象。
原理:至多只允许四个哲学家同时进餐,以保证至少有一个哲学家能够进餐,最终总会释放出他所使用过的两支筷子,从而可使更多的哲学家进餐。
semphore chopstick[5] = { 1,1,1,1,1 }
semphore r = 4;
void philosopher(int i) //哲学家编号,从0-4
{
while (true)
{
think(); //哲学家在思考
wait(r); //请求进餐
wait(chopstick[i]); //拿起左边筷子
wait(chopstick[(i + 1) % 5]); //拿起右边筷子
eat(); //吃饭
signal(chopstick[i]); //放下左边筷子
signal(chopstick[(i + 1) % 5]); //放下右边筷子
signal(r); //释放信号量r
}
}
方法二
仅当哲学家的左右手筷子都拿起时才允许进餐。
解法 1:利用 AND 型信号量机制实现。
原理:多个临界资源,要么全部分配,要么一个都不分配,因此不会出现死锁的情形。
semphore chopstick[5]={1,1,1,1,1}
void philosopher(int i) //哲学家编号,从0-4
{
while (true)
{
think(); //哲学家在思考
Swait(chopstick[i]; chopstick[(i + 1) % 5]); //同时拿起左边筷子和右边筷子
eat(); //吃饭
Ssignal(chopstick[i]; chopstick[(i + 1) % 5]); //同时放下左边筷子和右边筷子
think();
}
}
解法 2:利用信号量的保护机制实现。
原理:通过互斥信号量 mutex 对 eat() 之前取左侧和右侧筷子的操作进行保护,可以防止死锁的出现。
semphore chopstick[5]={1,1,1,1,1}
semphore mutex = 1;
void philosopher(int i) //哲学家编号,从0-4
{
while (true)
{
think(); //哲学家在思考
wait(&mutex);
wait(chopstick[i]); //拿起左边筷子
wait(chopstick[(i + 1) % 5]); //拿起右边筷子
signal(&mutex);
eat(); //吃饭
signal(chopstick[i]); //放下左边筷子
signal(chopstick[(i + 1) % 5]); //放下右边筷子
}
}
方法三
规定奇数号哲学家先拿左筷子再拿右筷子,而偶数号哲学家相反。
原理:按照下图,将是 2,3 号哲学家竞争 3 号筷子,4,5 号哲学家竞争 5 号筷子。1 号哲学家不需要竞争。最后总会有一个哲学家能获得两支筷子而进餐。
semphore chopstick[5] = { 1,1,1,1,1 }
void philosopher(int i) //哲学家编号,从0-4
{
while (true)
{
think(); //哲学家在思考
if (i % 2 == 0) //偶数哲学家,先右后左
{
wait(chopstick[(i + 1) % 5]); //拿起右边筷子
wait(chopstick[i]); //拿起左边筷子
eat(); //吃饭
signal(chopstick[(i + 1) % 5]); //放下右边筷子
signal(chopstick[i]); //放下左边筷子
}
else //奇数哲学家,先左后右
{
wait(chopstick[i]); //拿起左边筷子
wait(chopstick[(i + 1) % 5]); //拿起右边筷子
eat(); //吃饭
signal(chopstick[i]); //放下左边筷子
signal(chopstick[(i + 1) % 5]); //放下右边筷子
}
}
}
读者写者问题
参考:读者写者问题
1.问题描述
2.需要满足的条件
- 写进程与写进程之间必须互斥的写入数据(因为如果两个写进程同时对共享数据中的区域A中的数据进行写操作的话,会导致数据错误覆盖的问题)
- 写进程与读进程之间必须互斥的访问共享数据(因为写进程与读进程如果同时访问共享数据,可能会导致数据不一致的问题。比如:读进程A想要访问共享数据中的B数据,但是写进程C在读进程A访问B数据之前将B数据进行了更新,这就会导致读进程A读不到它想要读到的数据,从而出现数据不一致问题)
- 读进程与读进程之间可以同时访问数据,不需要实现互斥的访问共享数据(因为读进程读数据,并不会像之前的生产者消费者问题中的消费者那样改变数据或者是将数据清空,所以多个读进程可以同时访问共享数据)
3.代码
typedef int semphore
semphore mutex = 1; //控制对临界区的访问
semphore db = 1; //控制对数据库的访问
int rc = 0; //正在读或者即将读的进程数目
void reader()
{
while (true)
{
P(&mutex); //进入临界区
rc = rc + 1;
if (rc == 1) //如果是第一个读者,将控制访问数据库的权限
{
P(&db); //不让写者访问数据库
}
V(&mutex); //退出临界区
read_data_process(); //读取数据
P(&mutex); //进入临界区
rc = rc - 1; //将读者数减一
if (rc == 0) //如果是最后一个读者,表示写者可以访问数据库了
{
V(&db); //释放数据库的访问权限
}
V(&mutex); //退出临界区
}
}
void writer()
{
while (true)
{
P(&db); //获取互斥访问数据库
write_data_process(); //写数据
V(&db); //释放互斥访问
}
}
死锁问题
可能发生死锁的情况
typedef int semphore
semphore resource_1 = 1;
semphore resource_2 = 1;
void process_1()
{
P(&resource_1);
P(&resource_2);
process_A();
P(&resource_2);
P(&resource_1);
}
void process_2()
{
P(&resource_2);
P(&resource_1);
process_B();
P(&resource_1);
P(&resource_2);
}
最后
以上就是复杂金毛为你收集整理的操作系统经典问题的全部内容,希望文章能够帮你解决操作系统经典问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复