信号量学习笔记
来源:互联网 发布:java中for循环 编辑:程序博客网 时间:2024/06/05 17:32
忙等待显然非常的浪费CPU时间,但是比浪费CPU时间更严重的问题是忙等待会导致优先级反转问题(Priority inversion problem)。所谓优先级反转问题,就是当低优先级的进程占用了跟高优先级进程共享的资源,此时高优先级进程就绪了,根据调度规则只要高优先级出于就绪就可以运行它。但是低优先级进程占用了共享资源,于是高优先级进程开始忙等待,而低优先级进程得不到调度无法走出临界区释放共享资源,结果就导致高优先级进程一直忙等待。
为了避免忙等待带来的优先级反转问题,有个办法,那就是当进程无法进入临界区获取共享资源时,不是忙等待,而是被阻塞,既sleep和wakeup。
那么sleep和wakeup是否能很好的解决问题或者没有弊端?先看经典的生产者消费者问题:
#define N 100 /*缓冲区中的槽数目*/int count = 0; /*缓冲区中的数据项数目*/void producer(void){ int item; while(TRUE) { item=produce_item(); if(count==N) sleep(); insert_item(item); count=count+1; if(count==1) wakeup(consumer); /*count==1说明生产之前count==0,consumer进程应该在睡眠,遂唤醒它*/ }}void consumer(void){ int item; while(TRUE) { if(count==0) sleep(); item=remove_item(); count=count-1; if(count==N-1) wakeup(producer); /*count==N-1说明消费之前缓冲区满了,生产者应该在睡眠,遂唤醒*/ consume_item(item); }}
这里有可能会出现竞争条件,因为对count的方位未加限制。有可能出现以下情况:缓冲区为空,消费者刚刚读取count的值发现它为0.此时调度程序把 CPU切换给了生产者。生产者想缓冲区中加入一个数据项,count加1,并且它推断由于刚才count为0,所以消费者一定在睡眠,所以调用 wakeup来唤醒消费者。但消费者并未睡眠,所以wakeup信号丢失。当消费者下次运行时,它将测试先前读到的count值,发现为0,于是睡眠。生 产者迟早会填满整个缓冲区,然后睡眠。最后两个进程都将永远睡眠下去。
问题的实质在于发给一个尚未睡眠的进程的wakeup信号丢失了。好了,接下来信号量登场。
信号量(semaphore)是一个用来累计唤醒次数的整型变量,一个信号量的取值可以为0或正值。信号量有down和up两种操作。
down操作是指检查其值是否大于0,若大于0,将其减1并继续;若该值为0,进程睡眠,down操作结束。特别要注意的是,信号量是一种新的变量类型,它检查数值、修改变量值以及可能发生的睡眠操作均为一个单一的、不可分割的原子操作。
up操作是指对信号量增1.如果在对一个信号量做up之前,信号量值为0,并且有一个或多个进程想对该信号量做down操作,但因为信号量值为0导致进程睡眠,无法完成down操作,此时另外一个进程对该信号量做up操作,那么系统将选择其中一个睡眠进程允许其完成down操作,但是信号量值仍然为0,只不过在该信号量上睡眠的进程少了一个,因为让它完成了down操作。
用信号量解决了丢失的wakeup问题。为确保信号量能正确工作,最重要的是要采用一种不可分割的方式来实现它。通常是将up和down作为系统调用实现,而且系统只需要在执行以下操作使暂时屏蔽全部中断:测试信号量、更新信号量以及在需要时使某个进程睡眠。由于这些动作只需要几条指令,所以屏蔽中断不会带来什么副作用。如果使用多个CPU,则每一个信号量应该由一个锁变量进行保护,通过TSL或XCHG指令来确保同一时刻只有一个CPU在对信号量进行操作。
应该注意的是,使用TSL或XCHG忙等待另一个CPU对一个信号量的操作与使用TSL或XCHG忙等待另一个进程离开临界区是完全不同的,因为信号量的操作就是几条指令而已,时间是固定的几个毫秒,而等待另一个进程离开缓冲区的时间则是未知的,可能任意长。
下面是一个用信号量实现的生产者消费者代码:
#define N 100 /*缓冲区中的槽数目*/typedef int semaphore; /*信号量是一种特殊的整型数据*/semaphore mutex = 1; /*计数缓冲区的空槽数目*/semaphore empty = N; /*计数缓冲区的满槽数目*/void producer(void){ int item; while(TRUE) { item=produce_item(); down(&empty); /*空槽数目减一*/ down(&mutex); /*进入临界区*/ insert_item(item); up(&mutex); /*离开临界区*/ up(&full); /*将满槽的数目加1*/ }}void consumer(void){ int item; while(TRUE) { down(&full); down(&mutex); item=remove_item(); up(&mutex); up(&empty); consume_item(item); }}
- VxWorks学习笔记 -- 信号量
- 信号量学习笔记
- 信号量学习笔记
- 信号量学习笔记
- 信号量学习笔记
- Linux学习笔记之---信号量
- RTT学习笔记之信号量
- python学习笔记三---segmaphore信号量学习
- UCOS_II学习笔记---信号量管理之信号量的使用
- ipmsg学习笔记2—信号量
- 进程间通信学习笔记五(信号量)
- uC/OS-II 学习笔记之:信号量
- MFC学习笔记 线程之信号量
- uC/OS-II 学习笔记:信号量
- uC/OS-II 学习笔记:信号量实验
- FreeRTOS学习笔记——二值型信号量
- FreeRTOS学习笔记——互斥型信号量
- 闭锁、栅栏、交换机、信号量学习笔记
- 2014 编程之美 预赛第一场 活动中心 三分
- 选取命令:cut,grep
- wc命令
- Python风格规范
- OpenCV编程->图像复原(cvInpaint)
- 信号量学习笔记
- java enum的用法详解
- 解决vs2008非MFC工程依赖msvcr90.dll的问题
- NSString [a insertString: atIndex: n] 在第 n 位 插入新的字符串
- Understanding /etc/shadow file
- 如何用python最快的获取大文件的最后几行
- 电商应用再设计的九条经验
- [ACM] hdu 1166 敌兵布阵(树状数组)
- gradle android友盟多渠道混淆编译打包