最近双改变了兴趣方向,正在coursera上学习操作系统这门课程
看到了关于多进程的互斥问题
大体就是说考虑到多进程的程序可能会共用共同的某一部分内容,这部分称为临界区
但是由于cpu的调度,如果有两个进程A,B
A在使用临界区的时候,由于种种原因被下CPU换成了B上CPU执行
但是这个时候A对临界区的操作还没有完成与保存,与此同时B又开始了对临界区的操作
这样势必会造成临界区数据的混乱
举个例子
银行的两个ATM取款机(进程AB)上同时在对一个银行账户操作(临界区)
ATM机的逻辑是这样的
ATM(count, val){//账户 取现金额
read(count) //读入账户数据
if(count>val){ //如果账户余额充足
count -=val;
}
write(count)//将剩余余额写入
}
比如说这个账户一共有5000元,首先一个人在A上取现2000,但是在取现后写入之前
另一个人在B上同样取现了1000,这个时候A就被暂停运行了,此时的count还没有被重写
B机器上还是读到有5000的余额,取后还剩4000,写入。
之后A继续执行,但是A中现在count的值是3000,再执行写入操作
于是最终账户的余额成了3000,而不是应该的2000。
为了解决这个问题,我们需要让进程在临界区的操作上互斥
也就是说同一时间只能有一个进程对其操作
然后学得一神奇的方式,叫做信号量法,发明者-----dijkstra,对就是那个最短路的dijkstra
他的解决方法是
设置两个函数原子函数(不可被中断)放在临界区的开始和结束
P()
临界区
V()
函数是这样的
count设置为1
P(s)
{
s.count--;
if(s.count<0){
表明已经有其他进程,所以本进程被设为阻塞状态,加入到等待队列s.queue中等待重新调度
}
}
V(s)
{
s.count++;
if(s.count<=0){
表示有进程在等待队列中
此时本进程已经结束对临界区的使用,所以可以唤醒等待队列中的一个进程
使其加入就绪队列
}
}
这样第一个申请使用临界区的进程可以直接使用
其他的就会被加入到等待队列中
这种方式完美的解决了互斥问题,并且相比其他的解决方案这个办法居然还可以解决生产消费者问题
太太太太厉害了
之后会写一下怎么用这个方式解决生产消费者问题
生产者消费者问题:
基本概念是有两个进程,对同一个缓冲区进行操作,一个生产者进程负责向缓冲区写入数据,消费者进程负责从缓冲区提取数据。
所以这样就有3个问题产生
1缓冲区不能同时被两个进程操作
2缓冲区满的时候,生产者不应该继续生产
3缓冲区空的时候,消费者也不应给继续消费
对于这个问题可以有个基本的解决方法就是
(图片来自 北京大学coursera操作系统网课)
简单来说就是依靠一个变量count来判断是否为空和满,如果不能执行操作则,睡眠,当可以执行的时候唤醒睡眠的状态。
但是这边有一个问题就是
如果消费者 判断count==0之后,即将睡眠的时候,被下cpu了,这个时候生产者最后count+1之后==1,所以知道之前有消费者睡眠了,需要唤醒
然后切换到消费者,这个时候消费者执行的是之前没有执行完的sleep代码,结果又回去睡眠了
但是之后生产者不再认为消费者处于睡眠状态,则出现了错误--消费者一睡不醒。
但是!PV操作解决了这个问题
大体代码如下
(图片来自 北京大学coursera操作系统网课)
简单来说就是将之前所说的PV操作的技术由1变成了具体的数,这样就天才的解决问题了!!
同时对缓冲区进行一下保护。
不过其中的开始的P操作的顺序不能变,因为如果 变了后
先占用缓冲区,然后发现没法写/读,去唤醒另一个进程,但是另一个进程首先要判断是不是有人在占用缓冲区,答案是yes,所以再回去原来的进程
然后就这样死住了......
好了,笔记结束。