logo资料库

一个c++环形队列缓冲区.doc

第1页 / 共5页
第2页 / 共5页
第3页 / 共5页
第4页 / 共5页
第5页 / 共5页
资料共5页,全文预览结束
ringbuf{ buf[m axlen]; rptr ; wplr; 环形缓冲区该写操作的分析与实现 环 形 缓 冲区是嵌人式系统中一种重要的常用数据结构.在多任务环境下实时, 如果有多个读写任务.一般需要用信号量来保护多个任务共享的环形缓冲区。但 是如果只存在1个读任务和1个写任务,采取适当的措施可以避免使用信号量,从 而提高程序的执行效率,并且避免任务间竟争所造成的不一致性。 1 单线程下的实现 先 定 义一 个简单的环形缓冲区数据结构;基于这样 一个条件,当环形缓冲区满时,不能再往里写数据了。 struct uint8 uint8 uint8 } 数据元素是无符号8位整数,maxlen代表环形缓冲 区的最大长度,rptr为读指针,wptr为写指针。读写指 针初始化为rptr=wptr=0· 1.1 读操作的实现 BOOL readbuf(struct ringbuf *mybuf, uint8' readdata)I /队 列 为 空 if(mybuf一>rptr == mybuf-> wptr) return FALSE; else{ 把队列里有数据,读出来 'readdata = rrty buf->buf[mybuf->印tr]; mybuf-> rptr + +; //调 整 读 指 针 ,以防止读指针越界 mybuf-> rptr- mybuf->rptr/ maxlen; return TRUE ; } } 该函数在缓冲区为空时返回FALSE。在缓冲区有数据时,1次读1个敌据元素,存 放在readdata所指向的变量里,并返回TRUE. 2 写操作的实现 BOOL writebuf(struct ringbuf" mybuf ,uint8 writedata)] /队 列 为 满 if(mybuf->rptr--=((mybuf->wptr+l)%maxlen)) return FALSE; else{ /队 列 有 空 位 置,写数据 my bu f-> bu f[m ybuf->wptr]=writedata; my bu f-> w ptr + +; /调 整 写 指 针 ,以防止写指针越界 my bu f-> wp tr- m ybuf->wptr%maxlen; ret ur nT R U E;
} } 在 判 断 缓冲区是否满的条件中.之所以要摸maxlen, 是因为假设读写指针为 图1所示, wptr所指的位置为空。为了区分缓冲区满和空两种情况的判定条件,此种情况缓 冲区被认为是已满。如果不采取模运算,wptr+l!=rptr,将认为可以继续写数据, 从而当写操作结束时,读写指针相等,会误认为队列空。该函 数 在 缓冲区为 满时返回FALSE;在队列有空位 置时写人一个数据元素.并返回TRUE. 2 并发条件下如何控制竞争 2.1 防止竞争的必要性 假设 写 任 务进行写操作时在语句mybuf->wptr++执行完时被打断,如图2所示, 此时写指针wptr所处的位置是非法的。当系统被切换到读任务时,如果读任务有 读多个数据的企图,那么不但应该读出的数据被读出来了,而且当读指针被调整 为0时、会将以前已读出的数据重复读出来。这种出错行为是由于写指针的不正 确位置所导致的。假设 读 任 务进行读操作在语句mybuf->rptr+十执行完时被 打断,如图3所示,此时读指针rptr所处的位置是非法的。当系统被切换到写任 务时,如果写任务有写多个数据的企图那么当写指针指向缓冲区的末尾时,本来 缓冲区应处于满状态,不应再写了。但由于读指针所处的非法位置。在读任务获 得控制权之前,写任务认为缓冲区总是有空位置,将会租盖填写还没有读出去的 数据。 2.2 使用信号量 通过 上 述 分析可知,虽然读指针只由读任务来改写,写指针只由写任务来改 写;但是读任务要用到写指针,写任务要用到读指针,读写指针处于一个非法位
置时会给对方的判断造成错误。如果引人信号且机制,有效地保护临界区代码, 问题自然会得以避免。 2.3 改变读写指针的赎值方式 既 然 出 错都是由读写指针所处的非法位置引起的,那么修改读写指针时应注 意,不要赋非法位置。读写指针的值要么是还没有修改的要么是修改以后的正确 值,避免错误的值出现在读写指针中。另外还必须结合所使用的处理器,保证对 读写指针所定义的数据类型的赋值操作是原子性的。例如:如果所用处理器对16 位变量的赋值不是用一条指令实现的,需要分别对低字节和高字节进行操作那么 读写指针的数据类型就不能定义成16位的,或者说读写指针的使用范围应限制在 256以内。按照此种方法,假设处理器对8位变且的赋值是原子性的,读写操作可 以被改写为: BOOL readbuf(structri ngbuf* mybufu int8*re addata)I uin t8 t emp; /队 列为空 if(m y bu f一>rptr= =m ybuf一>wprt) ret unr F AL SE ; els e( /队 列 里 有 数 据,读出来 *er add at a=m y buf->buljmybuf->rprt); /j调 整 读 指 针 ,以防止读指针越界 tem p= m y bu f->rprt; tem p= ( tem p +l)0f omaxlen; my bu f-> 印 tr= temp; ret unr TR U E ; } } BOOL writebuf(structri ngbuf* mybufu int8w ritedata){ uin t8 t emp; /1队 列为满 if(m ybuf->rptr--((mybuf->wptr+l)%mmlen)) ret ur nF A LS E ; els e谭 /队 列 有 空 位 置,写数据 my bu f-> bu fjm ybuf->wprtl=writedata; /调 整 写 指 针 ,以防止写指针越界 tem p= m yb uf ->wprt; tem p= (t e m p+ 1)%maxlen; my bu f-> wp tr -temp; ert unr T R UE ; } } 引入 临 时 变量temp是为了进一步防范有些编译器在编译像 temp=( temp+1)%maxlen这种语句时,在最终赋值变且前有可能修改变里的值。 这样,无论读写操作在何处被打断,都不会引起出错。班
参考 文 献 I 昊 群 .实时任务处理程序设计中“易变的’。变量.单片 机与嵌入式系统应用,2003 (4):77-78 2 La bro sseJe anJ. uc/OS-II一源码公开的实时嵌入式操 作系统.邵贝贝译.北京:中国电力出版社,2001 3 赵 克 佳,沈志宇等.UNIX程序设计教程.北京:清 华大学出版社,2001 实现代码: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. package thread; public class CircularBuf { int NMAX=3; int iput = 0; /* 环形缓冲区的当前放人位置 */ int iget = 0; /* 缓冲区的当前取出位置 */ int n = 0; /* 环形缓冲区中的元素总数量 */ double buffer[]=new double[3]; /* 环形缓冲区的地址编号计算函数,,如果到达唤醒缓冲区的尾部,将绕回到头部。 环形缓冲区的有效地址编号为:0 到(NMAX-1) */ public int addring (int i){ return (i+1) == NMAX ? 0 : i+1; } /* 从环形缓冲区中取一个元素 */ public double get() { int pos; if (n>0){ pos = iget; iget = addring(iget); n--; System.out.println("get-->"+buffer[pos]); return buffer[pos]; }else { System.out.println("Buffer is Empty");
return 0.0; } } /* 向环形缓冲区中放人一个元素*/ public void put(double z){ if (n
分享到:
收藏