pv操作

pv操作是对信号量s的操作,最初由著名的计算机科学家Dijkstra提出,这里的phev是荷兰语,一个对应减一操作,一个是加一操作.在理论界称为p和v,这个是实现的方法,在工程上就是wait和signal,这个是操作的名称,内部实现是一样的,可读性更好
pv操作_第1张图片
这个是wait函数的实现,也就是p操作,信号量减一;
pv操作_第2张图片
这个是signal操作,对信号量加一;
pv操作_第3张图片
后面使用wait和signal代替p和v;wait和signal必须是原子操作(在这个操作执行的过程中不允许发生中断切换,整个操作必须完整的执行). 这个是它上课演示的例子:
pv操作_第4张图片
mutex在英文中的意思是互斥.
一个进程独自执行

  1. mutex最初为1,执行wait函数,先判断循环s<=0,不成立,执行减一操作,然后结束wait操作;,返回,进入临界区,此时mutex的值是0
  2. 然后执行signal操作,mutex的值又变成1.
    mutex具有锁的功能,一个进程进入临界区,就会将mutex置为0,出了临界区把它变为1,0表示加锁,1表示解锁.

多个进程
3. 首先都会执行wait操作,但是因为wait操作,也就是p操作是一个原子操作,所以只能有一个进程在调用wait的时候mutex是1, 然后把这个信号量mutex置为0,相当于这个进程进行了加锁,其它的进程在循环判断的时候信号量mutex的值是0,循环条件成立,所以会在循环那个位置一直等待,也就是在wait函数那里一直等待,直到将mutex置为0的那个进程执行完毕,通过signal操作将mutex重新置为1,相当于解锁,此时前面在wait函数中等待的一个进程可以结束循环,将mutex重新置为0,这个新的进程获得了锁.

具有Counting semaphore(计数信号量)功能,将mutex设置为2的话,可以同时有两个进程进入到临界区中,控制进入临界区的进程的数量.信号量在整个过程中不是只有1和0两种状态.将初始的mutex设置为1,那么只有两种状态,称为Binary semaphore(二制信号量),就是一个互斥锁的特性,信号量是一种包含性的设计.

信号量的实现.

wait个signal操作是一个原子操作,硬件指令尽量原始化,尽量简单,指令的分类:RISC和CISC.信号量不是通过硬件系统直接实现的,而是通过操作系统借助于软件的方法来实现的.硬件提供了两种最基本的支持,Disable/Enable Interrupts(开关中断)和TestAndSet().

pv操作_第5张图片
对于不能进入临界区的线程,会一直处在死循环中,CPU会一直被占用消耗,不做任何有意义的操作,这是一种浪费,这种无意义的等待称为Busy waitting(忙等),.忙等就是CPU不停的做一件毫无意义的事情,只是在等待某一个事件的发生,但是对CPU处于占用状态.这种设置实现的锁被称为
spin lock(自旋锁).

忙等处于run或者ready状态,

解决的方法是将忙等放在等待wait状态,知道锁被解开再唤醒.

pv操作_第6张图片
重新定义信号量,信号量是一个结构,有两个部分组成,一个是一个值,一个是一个表示进程的PCB类型的指针,哟个这个指针来构造一个链表,穿起来等待的进程,当信号量发生变化时,从这个连上选择一个进程唤醒.避免忙等,通常将等待的设备穿起来.

有两个操作,一个Block(阻塞进程),一个Wakeup(唤醒进程)

基于Block和Wakeup的wait和signal的实现:
pv操作_第7张图片
对比原来的实现:

pv操作_第8张图片

原来的wait是先等再执行减操作,现在是先减再执行等操作,有区别,但是区别不大.同一种操作的不同实现.

以二制信号量为例进行分析:

初始的时候value的值是1,先执行减一操作,value的值变成0,然后判断当前的value的值是否小于0,如果小于0就把当前的进程加入到链表的尾部,然后执行block将当前的进程阻塞,进入waitting状态;如果条件小于0不成立,就结束wait操作,进入临界区.

当第二个进程进入时,此时的value的值是0,先执行减一操作,value的值变成-1,-1<0,条件成立,将当前进程添加到链表的尾部,然后让当前的进程处于waitting状态;

当第三个进程进入时,此时value的值是-1,先执行减一操作,此时value的值变成-2,-2<0,再将当前的进程加入到链表的尾部,同时让当前的进程进入waitting等待状态.

第四个第五个…每个进程进来先减一,判断小于0,将当前进程加入到链表的尾部,然后将当前的进程设置为等待状态

当value<0的时候,value的值的绝对值就是链表上的进程的个数.

分析解锁操作:

首先将value的值加一,然后判断value的值是否<=0.
如果value的值在前面加一后大于1,说明最开始value的值是0,也就是此时的链表上没有其它使用这个信号量的处于等待的进程;当value加一后仍然小于等0,说明此时一定还有其他进程等着这个信号量,然后从链表中取出一个进程,然后通过wakeup方法把这个进程唤醒,进程醒来后就退出了,获得了信号量,进入了临界区.

三类典型的问题:
pv操作_第9张图片

  • 生产者—消费者问题
  • 读者—写者问题
  • 哲学家就餐问题
  • 生产者–消费者问题(最典型的问题)

最简单情形的描述:

两个进程,一个生产者,一个消费者,生产者产生数据,将数据放到两个进程共享的缓冲区中,消费者从缓冲区中取出数据,对数据进行消费(处理).

一个进程自己生产消费和变成两个进程一个生产一个消费,两者的并行性不同.生产者只要生产,消费者只要缓冲区有数据就可以消费.把串行的变成并行的,两两之间构成生产者-消费者,类似于计组中的流水线.流水线上每个组件的数目需要根据每个环节的难度强度.

缓冲区n个
pv操作_第10张图片
定义三个信号量,mutex,full和empty,full表示有数据的格子的个数,初始时候没有数据,empty表示空的格子的个数.

生产者:
pv操作_第11张图片
消费者:
pv操作_第12张图片

mutex的作用:
保护进程共享缓冲区数据的作用,对共享的公共资源进行保护,保护buffer,获得互斥锁才能操作,完成操作后解锁.

最开始生产之生产,empty是n,wait(empty)不会阻塞,empty减一,进入缓冲区写数据,空格少了一个;
多个生产者生产数据,第n+1个生产者wait(empty)会阻塞.
保证不会出现多个生产者像一个格子写数据.

你可能感兴趣的:(pv操作)