[操作系统学习笔记(4)] 进程同步,信号量
2016-10-05 11:48
344 查看
进程同步
让多进程合作更加有序。在执行程序的时候,顺序并不是随意的,因为一个进程中间可能出现堵塞,这时需要停下来,并等待别的进程发来信号,再继续执行。
接下来,我们需要建立生产者 - 消费者模型,来表述两个进程之间的合作,一个进程“放入”数据,一个进程“使用”数据:
#define BUFFER_SIZE 10 typedef struct{...} item;//数据的结构 int in = out = counter = 0;//队尾,队首,计数器
对于生产者:
while(true) { while(counter == BUFFER_SIZE); //如果数据区放满了,停止生产 buffer[in] = item; //在队尾放入item in = (in + 1)%BUFFER_SIZE;//队尾后移一位 count++;//计数器加一 }
对于消费者:
while(true) { while(counter == 0); //如果数据区空了,等待数据 item = buffer[out]; //从队首取出item out = (out + 1)%BUFFER_SIZE; //队首后移一位 counter--; //计数器减一 }
以上代码进一步可以改写为:当消费者没有东西可以消费时,沉睡,而生产者生产出第一个物体时,唤醒消费者;当生产者生产的东西已经达到缓存所能容纳量,沉睡,而消费者使用物体使得缓存区不再满时,唤醒生产者。
对于生产者:
while(true) { if(counter == BUFFER_SIZE) sleep(); counter++; if(counter == 1) wakeup(consumer); }
对于消费者:
while(true) { if(counter == 0)sleep(); counter--; if(counter == BUFFER_SIZE - 1) wakeup(producer); }
但是,这样仍然是不够的。我们假设两个生产者的情况:
缓冲区已满,生产者P1沉睡,生产者P2也沉睡。消费者一次循环后,counter为BUFFER_SIZE - 1,发信号唤醒P1。再消费一次,变为BUFFER_SIZE - 2,所以P2没有按照预期被唤醒。这是存在问题的。
信号量
在这种情况下,我们需要引入一个概念——信号量(semaphore)。信号量本身存储一些信息,并且我们会根据这个信息,来决定相应对象需要被唤醒还是需要睡眠。
在这个例子中,信号量为n,若n>0,代表当前资源数量为n,若n = 0,则代表没有资源,若n < 0,则代表有n个消费者进程在等待资源。
这样一来,再看上面的那个例子。对于两个生产者P1,P2,缓冲区满后,生产者P1睡眠,信号量为-1,生产者P2睡眠,信号量为-2。之后消费者唤醒P1,信号量为-1,再唤醒P2,信号量为0。
struct semaphore { int value; //记录资源个数 PCB *queue; //记录等待在该信号量上的进程 }
与信号量对应的两个函数:生产函数与消费函数。
P(semaphore s);//消费资源 V(semaphore s);//产生资源 P(semaphore s) { s.value--; if(s.value<0){ sleep(s.queue); } } V(semaphore s) { s.value++; if(s.value<=0) wakeup(s.queue); }
保护信号量
但是,这是否代表了使用信号量就不会出任何差错呢?我们需要考虑到可能产生的问题。我们列出信号量修改时生产者p1和p2的举动,它涉及到了寄存器。
生产者p1 register =empty; register =register – 1; empty =register; |
生产者p2 register =empty; register =register – 1; Empty =register; |
但是,所有指令并不一定按我们所想的顺序执行,如果它们按如下顺序执行(这是完全可能的):
p1.register =empty; p1.register =p1.register – 1; p2.register =empty; p2.register =p2.register – 1; empty =p1.register; empty =p2.regster; |
一个直观的想法是:上锁。也就是说,在一个进程写变量empty的时候,禁止其他进程访问empty。
在有锁的情况下,我们再尝试按之前的顺序执行以上操作:
生产者p1 检查并给empty上锁 p1.register =empty; p1.register =p1.register – 1; 生产者p2 检查empty的锁 生产者p1 empty =p1.register; 给empty开锁 生产者P2 检查并给empty上锁 |
信号量临界区
所谓信号量临界区,也就是一次只允许一个进程进入的一段代码。所以,我们面临的最大问题是:如何找出进程中的临界区代码。
实现好的临界区,有三个基本要求:
① 互斥进入:一个进程在临界区,其它进程不能进入。
② 有空让进:若干进程要求进入空闲临界区时,应尽快使一进程进入临界区。
③ 有限等待:从进程发出进入请求到允许进入,不能无限等待。
轮换法
轮换法,也就是给每个人一个编号,一个进程执行完后,更换当前编号(turn),交由下一个进程执行。一个进程如果想要执行临界区代码,首先它需要等待turn变量等于它自身的编号。
一个进程如果完成了临界区代码,它需要把turn变量改成其它编号。
很显然,在轮换法机制下,进程是互斥进入临界区的, 因为只有编号等于turn的进程才能执行。
但是,它是否满足有空让进呢?假设这样一个情况,turn = 1,而进程P1并不调度。我们知道进程在完成调度后才会修改turn,这时P2恰好又在等待使用临界区,结果因为没有轮到它,它也无法执行。最终的结果是没有人使用临界区。
标记法
标记法,也就是当一个进程进入临界区时,令自己的标记(flag)为真,等到其它进程标记不为真后,开始执行;等到使用完毕后,再令标记为假。进程P0: flag[0] = true; while(flag[1]);//等待 //进入 flag[0] = false; |
进程P1: flag[1] = true; while(flag[0]); //等待 //进入 flag[1] = false; |
那么,我们再来考虑它是否满足有空让进,如果它按照以下顺序执行:
flag[0] = true flag[1] = true while(flag[1]); while(flag[0]); |
非对称标记(Peterson算法)
一种想法是,能不能结合以上两种方法(轮转法和标记法),实现更好的算法。在这种情况下,我们再来看看两个进程p0,p1进入临界区的伪代码:
进程p0: flag[0] = true; turn = 1; while(flag[1]&& turn == 1);//等待 //进入 flag[0] = false; |
进程p1: flag[1] = true; turn = 0; while(flag[0]&& turn == 0);//等待 //进入 flag[1] =false; |
其次,它也满足有空让进,考虑进程p1不在临界区的情况,这时有flag[1] = false(也就是还没执行flag[1] = true), 或者 turn = 0(也就是已经执行了turn=0,进入等待),这时p0是可进入的。
最终,它满足有限等待,如果p0想要进入临界区,p0不会等待很久,因为轮换,p1最多执行一次。
面包店算法
之前讨论的都是两个进程,如果是多个进程,那么可以简单修改为:轮转:每个进程都获得一个序号,序号最小的进入
标记:进程离开时序号为0,不为0即标记。
它同样也满足互斥进入,有空让进,有限等待。
中断
临界区只允许一个进程进入,换个角度想,也就是在这个进程执行的时候,我们需要禁止其它进程的调度。因为只有通过调度才能切换。换言之,就是在执行临界区时,关闭中断,其它进程无法进入,离开后,再打开中断。
这个方法仅仅适用于单CPU,因为多CPU有多个中断寄存器。
硬件原子指令
再换一个角度想,我们实际上是要保证执行临界区代码时,其它进程不切入,也就是说,我们需要把临界区代码作为一个原子操作,它只有两个状态,做与不做,而不存在做了一半。那么我们可以把这个指令设计到硬件中。相关文章推荐
- UCOS_II学习笔记---信号量管理之信号量的使用-进程(任务)同步
- 操作系统学习笔记-信号量及PV操作与进程互斥和消费者生产者问题
- 操作系统学习笔记六:进程同步
- 操作系统学习笔记:进程同步
- 操作系统概念学习笔记 12 进程同步(二)管程
- 操作系统学习笔记(9) 互斥和同步的信号量算法
- 操作系统学习笔记:进程同步
- 操作系统学习笔记(28)--初始化进程调度
- 2011/5/28操作系统学习笔记之经典同步问题
- Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
- 操作系统学习笔记——进程的状态
- 操作系统学习笔记——进程互斥与同步
- 操作系统精髓与设计原理学习笔记三:进程描述和控制
- 学习笔记——操作系统_Linux进程通信之消息队列
- 学习笔记 --- LINUX的同步互斥机制 --- 自旋锁与信号量的区别
- 操作系统课堂笔记(4)进程管理之进程同步与互斥
- Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)
- 操作系统原理学习笔记--进程管理
- 操作系统学习笔记(8) 互斥和同步的实现算法
- 操作系统学习笔记------进程描述与进程状态变化