文章目录
1 环形队列是什么
队列是一种常用的数据结构,这种结构保证了数据是按照“先进先出”的原则进行操作的,即最先进去的元素也是最先出来的元素.环形队列是一种特殊的队列结构,保证了元素也是先进先出的,但与一般队列的区别是,他们是环形的,即队列头部的上个元素是队列尾部,通常是容纳元素数固定的一个闭环。但是实际上还是一个
2环形队列的优缺点
1、 保证元素是先进先出的
2、元素空间可以重复利用
3、为多线程数据通信提供了一种高效的机制。
在最典型的生产者消费者模型中,如果引入环形队列,那么生成者只需要生成“东西”然后放到环形队列中即可,而消费者只需要从环形队列里取“东西”并且消费即可,没有任何锁或者等待,巧妙的高效实现了多线程数据通信。
3环形队列的应用场景
应用于需要高效且频繁进行多线程通信传递数据的场景
dpdk中用的ringbuffer环形队列,给dpdk提供高效的线程间通信,其ringbuffer做的比较高效,因为其环形队列的出队列和进队列只有一个临界值的++操作,这个操作是具有原子性的,底层是汇编指令集实现的++原子操作。
4环形队列的原理分析
环形队列其实质还是一块内存空间,只不过在队尾塞满数据后,就还是从最开始的地方开始处理数据。
判断队列为空
font == rear
判断队列满了
(rear+1) % maxSize = font.
下面的代码实现有两个版本的,
1 支持一写一读的生产消费模式
2 支持些多读的生产消费模式
5环形队列的代码分析和实现
版本一
typedef struct ringbuffer_s {
char * buf;
unsigned int size; // buffer 的大小
unsigned int write_pos; // 下一个可写的位置
unsigned int read_pos; // 下一个可读的位置
} ringbuffer_t;
环形队列结构
初始化
void rb_init(ringbuffer_t *r, unsigned int sz) {
if (!is_power_of_two(sz)) sz = roundup_power_of_two(sz);
r->buf = (char *)malloc(sz * sizeof(char));
r->size = sz;
r->write_pos = 0;
r->read_pos = 0;
}
环形队列初始化
1 申请环形队列的存储数据的空间
2 设置环形队列能够存储多大的数据量。
4 设置读和写 的位置索引
读取数据
unsigned int rb_read(ringbuffer_t *r, char* buf, unsigned int sz) {
if (rb_isempty(r)) {
// buffer 为空
return 0;
}
unsigned int i;
sz = min(sz, r->write_pos - r->read_pos);
#ifdef USE_BARRIER
// 确保在开始移动buffer的时候,先采样好 write_pos
smp_rmb();
#endif
i = min(sz, r->size - (r->read_pos & (r->size - 1)));
memcpy(buf, r->buf+(r->read_pos & (r->size - 1)), i);
memcpy(buf+i, r->buf, sz-i);
#ifdef USE_BARRIER
// 确保移动数据在修改 read_pos 之前
smp_mb();
#endif
r->read_pos += sz;
return sz;
}
smp_mb() 设置读内存屏障的汇编指令函数,
将环形队列中的数据取出来然后就,将读的索引加取出来数据的大小。
写入数据
unsigned int rb_write(ringbuffer_t *r, char* buf, unsigned int sz) {
if (sz > rb_remain(r)) {
// 剩余空间不足
return 0;
}
#ifdef USE_BARRIER
// 确保在开始移动buffer的时候,先采样好 read_pos
smp_mb();
#endif
unsigned int i;
i = min(sz, r->size - (r->write_pos & (r->size - 1)));
memcpy(r->buf + (r->write_pos & (r->size - 1)), buf, i);
memcpy(r->buf, buf+i, sz-i);
#ifdef USE_BARRIER
// 确保移动数据在修改 write_pos 之前
smp_wmb();
#endif
r->write_pos += sz;
return sz;
}
将数据写如环形队列,将write_idx的大小加上size。
这个版本没有用到锁,其代码简洁高效,支持一写一读的场景下。
版本二
环形队列的结构
struct ring_buffer
{
void *buffer; //缓冲区
uint32_t size; //大小
uint32_t in; //入口位置
uint32_t out; //出口位置
pthread_mutex_t *f_lock; //互斥锁
};
相比于版本一多了一个互斥锁,可以用于多线程读写。其性能和代码简度比第一个版本差一些。反正说各有特色把。
环形队列的初始化
//初始化缓冲区
struct ring_buffer* ring_buffer_init(void *buffer, uint32_t size, pthread_mutex_t *f_lock)
{
assert(buffer);
struct ring_buffer *ring_buf = NULL;
#if 0
if (!is_power_of_2(size))
{
fprintf(stderr,"size must be power of 2.\n");
return ring_buf;
}
#endif
ring_buf = (struct ring_buffer *)malloc(sizeof(struct ring_buffer));
if (!ring_buf)
{
Console_Error("Failed to malloc memory,errno:%u,reason:%s",
errno, strerror(errno));
return ring_buf;
}
memset(ring_buf, 0, sizeof(struct ring_buffer));
ring_buf->buffer = buffer;
ring_buf->size = size;
ring_buf->in = 0;
ring_buf->out = 0;
ring_buf->f_lock = f_lock;
return ring_buf;
}
读取环形队列数据
uint32_t __ring_buffer_get(struct ring_buffer *ring_buf, void * buffer, uint32_t size)
{
assert(ring_buf || buffer);
uint32_t len = 0;
size = min(size, ring_buf->in - ring_buf->out);
uint32_t RingbufOutRemainder = (ring_buf->out) % (ring_buf->size);
/* first get the data from fifo->out until the end of the buffer */
len = min(size, ring_buf->size - RingbufOutRemainder);
memcpy(buffer, ring_buf->buffer + RingbufOutRemainder, len);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + len, ring_buf->buffer, size - len);
ring_buf->out += size;
return size;
}
写入环形队列
uint32_t __ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
{
assert(ring_buf || buffer);
uint32_t len = 0;
//其实不用下面两句判断环形队列满
// if(__ring_buffer_len(ring_buf) >= ring_buf->size)
// return 0;//环形队列已满
size = min(size, ring_buf->size - ring_buf->in + ring_buf->out);//size变成环形缓存区最大可以字节数。
uint32_t RingbufInRemainder = (ring_buf->in) % (ring_buf->size);
/* first put the data starting from fifo->in to buffer end */
len = min(size, ring_buf->size - RingbufInRemainder);
memcpy(ring_buf->buffer + RingbufInRemainder, buffer, len);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(ring_buf->buffer, buffer + len, size - len);
ring_buf->in += size;
return size;
}
源码地址
版本一二地址:
https://github.com/xiaoyeyihao/xioayeyihao.github.io/tree/master/circular_queue