当前位置:
文档之家› 环形缓冲区的实现原理(ring buffer)
环形缓冲区的实现原理(ring buffer)
printf(“Buffer is empty\n”); return 0.0; } /* 向环形缓冲区中放入一个元素*/ void put(double z) { if (n<NMAX){ buffer[iput]=z; iput = addring(iput); n++; } else printf(“Buffer is full\n”); } int main{void) { chat opera[5]; double z; do { printf(“Please input p|g|e?”); scanf(“%s”, &opera); switch(tolower(opera[0])){ case ‘p’: /* put */ printf(“Please input a float number?”);
到项目的针对性简单性,实现了一个简单的环形缓冲队列,比 STL 的 vector 简单 PS: 第一次使用模板,原来类模板的定义要放在.h 文件中, 不然会出现连接错误。 template <class _Type> class CShareQueue { public: CShareQueue(); CShareQueue(unsigned int bufsize); virtual ~CShareQueue(); _Type pop_front(); bool push_back( _Type item); //返回容量 unsigned int capacity() { //warning:需要外部数据一致性 return m_capacity; } //返回当前个数 unsigned int size() { //warning:需要外部数据一致性 return m_size; } //是否满//warning: 需要外部控制数据一致性 bool IsFull() { return (m_size >= m_capacity); } bool IsEmpty() { return (m_size == 0); }
pBuf = new _Type[512];//默认 512 m_capacity = 512; } template <class _Type> CShareQueue<_Type>::CShareQueue(unsigned int bufsize) : m_head(0), m_tail(0) { if( bufsize > 512 || bufsize < 1) { pBuf = new _Type[512]; m_capacity = 512; } else { pBuf = new _Type[bufsize]; m_capacity = bufsize; } } template <class _Type> CShareQueue<_Type>::~CShareQueue() { delete[] pBuf; pBuf = NULL; m_head = m_tail = m_size = m_capacity = 0; } //前面弹出一个元素 template <class _Type> _Type CShareQueue<_Type>::pop_front() { if( IsEmpty() ) { return NULL; } _Type itemtmp; itemtmp = pBuf[m_head]; m_head = (m_head + 1) % m_capacity; --m_size; return itemtmp; } //从尾部加入队列 template <class _Type>
scanf(“%lf”, &z); put(z); break; case ‘g’: /* get */ z = get(); printf(“%8.2f from Buffer\n”, z); break; case ‘e’: printf(“End\n”); break; default: printf(“%s - Operation command error! \n”, opera); }/* end switch */ }while(opera[0] != ’e’); return 0; } 在 CAN 通信卡设备驱动程序中,为了增强 CAN 通信卡的通信能力、提高通信效率,根据 CAN 的特点,使 用两级缓冲区结构,即直接面向 CAN 通信卡的收发缓 冲区和直接面向系统调用的接收帧缓冲区。 通讯中 的收发缓冲区一般采用环形队列(或称为 FIFO 队列),使用环形的缓冲区可以使得读写并发执行,读进程 和写进程可以采用“生产者和消费者”的模型来 访问缓冲区,从而方便了缓存的使用和管理。然而,环形缓 冲区的执行效率并不高,每读一个字节之前,需要判断缓冲区是否为空,并且移动尾指针时需要进行“折行 处理”(即当指针指到缓冲区内存的末尾时,需要新将其定向到缓冲区的首地址);每写一个字节之前,需 要判断缓区是否为,并且移动尾指针时同样需要进行“ 折行处理”。程序大部分的执行过程都是在处理个别 极端的情况。只有小部分在进行实际有效的操作。这就是软件工程中所谓的“8 比 2”关系。结合 CAN 通讯 实际情况,在本设计中对环形队列进行了改进,可以较大地提高数据的收发效率。 由于 CAN 通信卡上接 收和发送缓冲器每次只接收一帧 CAN 数据,而且根据 CAN 的通讯协议,CAN 控制器的发送数据由 1 个字 节的标识符、一个字节的 RTR 和 DLC 位及 8 个字节的数据区组成,共 10 个字节;接收缓冲器与之类似, 也有 10 个字节的寄存器。所以 CAN 控制器收的数据是短小的定长帧(数据可以不满 8 字节)。 于是, 采用度为 10 字节的数据块业分配内存比较方便,即每次需要内存缓冲区时,直接分配 10 个字节,由于这 10 个字节的地址是线性的,故不需要进行“折行”处理。更重要的是,在向缓冲区中写数据时,只需要判断
一次是否有空闲块并获取其块首指针就可以了,从而减少了重复性的条件判断,大大提高了程序的执行效 率;同样在从缓冲队列中读取数据时,也是一次读取 10 字节的数据块,同样减少了重复性的条件判断。 在 CAN 卡驱动程序中采用如下所示的称为“Block_Ring_t”的数据结构作为收发数据的缓冲区: typedef struct { long signature; unsigned char *head_p; unsigned char *tail_p; unsigned char *begin_p; unsigned char *end_p; unsigned char buffer [BLOCK_RING_BUFFER_SIZE]; int usedbytes; }Block_Ring_t; 该数据结构在通用的环形队列上增加了一个数据成员 usedbytes,它表示当前缓冲区中有多少字节的空间 被占用了。使用 usedbytes,可以比较方 便地进行缓冲区满或空的判断。当 usedbytes=0 时,缓冲区空; 当 usedbytes=BLOCK_RING_BUFFER_SIZE 时,缓冲区 满。 本驱动程序除了收发缓冲区外,还有一个接 收帧缓冲区,接收帧队列负责管理经 Hilon A 协议解包后得到的数据帧。由于有可能要同接收多个数据帧, 而根据 CAN 总线遥通信协议,高优先级的报文将抢占总线,则有可能在接收一个低优先级且被分为 好几 段发送的数据帧时,被一个优先级高的数据帧打断。这样会出现同时接收到多个数据帧中的数据包,因而 需要有个接收队列对同时接收的数据帧进行管理。 当有新的数据包到来时,应根据 addr(通讯地址),mo de(通讯方式),index(数据包的序号)来判断是否是新的数据帧。如果是,则开辟新的 frame_node; 否则如果已有相应的帧节点存地,则将数据附加到该帧的末尾;在插入数据的同时,应该检查接收包的序 号是否正确,如不正确将丢弃这包 数据。 每次建立新的 frame_node 时,需要向 frame_queue 申请内存 空间;当 frame_queue 已满时,释放掉队首的节点(最早接收的但未完 成的帧)并返回该节点的指针。 当系统调用读取了接收帧后,释放该节点空间,使设备驱动程序可以重新使用该节点。 形缓冲区:环形缓冲队列学习 来源: 发布时间:星期四, 2008 年 9 月 25 日 浏览:117 次 评论:0 项目中需要线程之间共享一个缓冲 FIFO 队列,一个线程往队列中添数据,另一个线程取数据(经典的生产 者-消费者问题)。开始考虑用 STL 的 vector 容器, 但不需要随机访问,频繁的删除最前的元素引起内存 移动,降低了效率。使用 LinkList 做队列的话,也需要频繁分配和释放结点内存。于是自己实现一个有 限 大小的 FIFO 队列,直接采用数组进行环形读取。 队列的读写需要在外部进程线程同步(另外写了一个 RWGuard 类, 见另一文)
个
数
据
。
2、实例:环形缓冲区的实现 环形缓冲区是数据通信程序中使用最为广泛的数据结构之一,下面的代码,实现了一个环形缓冲区: /*ringbuf .c*/ #include<stdio. h>
#include<ctype. h> #define NMAX 8 int iput = 0; /* 环形缓冲区的当前放入位置 */ int iget = 0; /* 缓冲区的当前取出位置 */ int n = 0; /* 环形缓冲区中的元素总数量 */ double buffer[NMAX]; /* 环形缓冲区的地址编号计算函数,如果到达唤醒缓冲区的尾部,将绕回到头部。 环形缓冲区的有效地址编号为:0 到(NMAX-1) */ int addring (int i) { return (i+1) == NMAX ? 0 : i+1; } /* 从环形缓冲区中取一个元素 */ double get(void) { int pos; if (n>0){ Pos = iget; iget = addring(iget); n--; return buffer[pos]; } else {