一个RINGBUFFER的C代码实现

219 篇文章 35 订阅
2 篇文章 0 订阅
本文介绍了环形缓冲区(Ring Buffer)和循环队列的数据结构实现,包括C语言实现的环形缓冲区代码,以及严蔚敏数据结构中循环队列的示例。环形缓冲区常用于多线程通信,而循环队列则是高效的数据存储和检索工具。代码展示了初始化、读写操作和状态检查等功能,并提供了测试用例。
摘要由CSDN通过智能技术生成

头文件

#ifndef __RING_BUFFER__
#define __RING_BUFFER__
#include <stdint.h>
#include <stddef.h>
#include <pthread.h>
#include <unistd.h>

struct ringbuffer
{
    uint8_t *buffer_ptr;

    uint16_t read_mirror : 1;
    uint16_t read_index : 15;

    uint16_t write_mirror : 1;
    uint16_t write_index : 15;

    int16_t buffer_size;
    pthread_mutex_t mutex_lock;
};

enum ringbuffer_state
{
    RINGBUFFER_EMPTY,
    RINGBUFFER_FULL,
    RINGBUFFER_HALFFULL,
    RINGBUFFER_INVALID,
};

void ringbuffer_init(struct ringbuffer *rb, uint8_t *pool, int16_t size);
void ringbuffer_reset(struct ringbuffer *rb);
int16_t ringbuffer_put(struct ringbuffer *rb, const uint8_t *ptr, int16_t length);
int16_t ringbuffer_get(struct ringbuffer *rb, uint8_t *ptr, int16_t length);
int16_t ringbuffer_data_len(struct ringbuffer *rb);

struct ringbuffer* ringbuffer_create(int16_t length);
void ringbuffer_destroy(struct ringbuffer *rb);

static inline int16_t ringbuffer_get_size(struct ringbuffer *rb)
{
   return rb->buffer_size;    
}

#define ringbuffer_space_len(rb) ((rb)->buffer_size - ringbuffer_data_len(rb))

#endif

实现源码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ringbuffer.h"
 
void ringbuffer_init(struct ringbuffer *rb, uint8_t *pool, int16_t size)
{
    if(rb == NULL)
    {
        return;
    }
 
    rb->read_mirror = rb->read_index = 0;
    rb->write_mirror = rb->write_index = 0;
 
    rb->buffer_ptr = pool;
    rb->buffer_size = size;
    pthread_mutex_init(&rb->mutex_lock,NULL);
 
    return;
}
 
 
void ringbuffer_reset(struct ringbuffer *rb)
{
    if(rb == NULL)
    {
        return;
    }
    
    rb->read_mirror = 0;
    rb->read_index = 0;
    rb->write_mirror = 0;
    rb->write_index = 0;
 
    return;
}
 
 
int16_t ringbuffer_put(struct ringbuffer *rb, const uint8_t *ptr, int16_t length)
{
    int16_t size; 
 
    if(rb == NULL || length == 0)
    {
        return 0;
    }
    
    pthread_mutex_lock(&rb->mutex_lock);
    size = ringbuffer_space_len(rb);
    pthread_mutex_unlock(&rb->mutex_lock);
 
    if(size == 0)
        return 0;
 
    if (size < length)
    {
        length = size;
    }
 
    if (rb->buffer_size - rb->write_index > length)
    {
        memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
        rb->write_index += length; 
        return length;
    }
 
    memcpy(&rb->buffer_ptr[rb->write_index],&ptr[0],rb->buffer_size - rb->write_index);
    memcpy(&rb->buffer_ptr[0],&ptr[rb->buffer_size - rb->write_index],length - (rb->buffer_size - rb->write_index));
 
    pthread_mutex_lock(&rb->mutex_lock);
    rb->write_mirror = ~rb->write_mirror;
    rb->write_index = length - (rb->buffer_size - rb->write_index);
    pthread_mutex_unlock(&rb->mutex_lock);
 
    return length;
}
 
int16_t ringbuffer_get(struct ringbuffer *rb, uint8_t *ptr, int16_t length)
{
    if(rb == NULL || length == 0)
    {
        return 0;
    }
    
    int16_t size; 
    
    pthread_mutex_lock(&rb->mutex_lock);
    size = ringbuffer_data_len(rb);
    pthread_mutex_unlock(&rb->mutex_lock);
 
    if (size == 0) return 0;
 
    if (size < length)
    {
        length = size;
    }
 
    if (rb->buffer_size - rb->read_index > length)
    {
        memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
        rb->read_index += length;
        return length;
    }
 
    memcpy(&ptr[0],&rb->buffer_ptr[rb->read_index],rb->buffer_size - rb->read_index);
    memcpy(&ptr[rb->buffer_size - rb->read_index], &rb->buffer_ptr[0], length - (rb->buffer_size - rb->read_index));
 
    pthread_mutex_lock(&rb->mutex_lock);
    rb->read_mirror = ~rb->read_mirror; 
    rb->read_index = length - (rb->buffer_size - rb->read_index);
    pthread_mutex_unlock(&rb->mutex_lock);
 
    return length;
}
 
enum ringbuffer_state ringbuffer_status(struct ringbuffer *rb) 
{
    if (rb->read_index == rb->write_index)
    {
        if (rb->read_mirror == rb->write_mirror)
        {
            return RINGBUFFER_EMPTY;
        }
        else
        {
            return RINGBUFFER_FULL;
        }
    }
 
    return RINGBUFFER_HALFFULL;
}
 
int16_t ringbuffer_data_len(struct ringbuffer *rb)
{
    switch (ringbuffer_status(rb)) 
    {
        case RINGBUFFER_EMPTY:
            return 0;
        case RINGBUFFER_FULL:
            return rb->buffer_size; 
        case RINGBUFFER_HALFFULL:
        default:
            if (rb->write_index > rb->read_index)
                return rb->write_index - rb->read_index;
            else
                return rb->buffer_size - (rb->read_index - rb->write_index); 
    }
}

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include "ringbuffer.h"

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

struct ringbuffer *rb;
void *writer_thread(void *args)
{
    uint8_t w = 0;
    while(1)
    {
        /*pthread_mutex_lock(&mutex);*/
        if(ringbuffer_put(rb, &w, 1) == 1)
	    w ++;
        /*pthread_mutex_unlock(&mutex);*/
    }

    return NULL;
}

void *reader_thread(void *args)
{
    uint8_t r;
    while(1)
    {
        /*pthread_mutex_lock(&mutex);*/
        if (ringbuffer_get(rb, &r, 1) == 1)
            printf("---%x---\n", r);
        /*pthread_mutex_unlock(&mutex);*/
    }

    return NULL;
}

int main(void)
{
    unsigned int rc;

#if 1
    uint8_t *buffer = (uint8_t *)malloc(1024 + sizeof(struct ringbuffer) + 16);

    if(buffer == NULL) return -1;
    if((unsigned long)buffer & 7 != 0)
    {
        printf("%s line %d not aligned.\n", __func__, __LINE__);
	return -1;
    }
   
    rb=(struct ringbuffer *)buffer;

    if(rb == NULL) return -1;
    ringbuffer_init(rb, buffer + sizeof(struct ringbuffer), 1024);
    printf("buffer = %p, sizeof(rb) = %ld.\n", buffer, sizeof(struct ringbuffer));

#else
    static struct ringbuffer rbb;

    uint8_t *buffer = (uint8_t *)malloc(1024);
    if(buffer == NULL) return -1;
    rb = &rbb;
    memset(rb, 0x00,sizeof(struct ringbuffer));
    memset(buffer, 0x00,1024);
    ringbuffer_init(rb, buffer, 1024);
#endif

    pthread_t writer;
    rc = pthread_create(&writer, NULL, writer_thread, NULL);
    if (rc)
    {
        printf("ERROR; return code is %d\n", rc);
        return EXIT_FAILURE;
    }

    pthread_t reader;
    rc = pthread_create(&reader, NULL, reader_thread, NULL);
    if (rc)
    {
        printf("ERROR; return code is %d\n", rc);
        return EXIT_FAILURE;
    }

    pthread_join(reader, NULL);
    pthread_join(writer, NULL);
    return EXIT_SUCCESS;
}

Makefile

all:
	gcc main.c ringbuffer.c -o main -lpthread

测试:

另外,Linux内核也有一个不错的实现,参考:

./linux-5.4.99/drivers/media/dvb-core/dvb_ringbuffer.c

如上图所说,严蔚敏数据结构中的循环队列实现,少用一个空间的方法,约定队列头指针在尾指针的下一位上作为队列满状态的标志,而本文开头的循环队列实现,则是使用read_mirror_index, write_mirror_index两个标志位来判定是否发生了回绕。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXQSIZE 100

typedef struct {
	unsigned long *base;
	int front;
	int rear;
} user_queue_t;

static user_queue_t uqueue;

int init_queue(user_queue_t *q)
{
	q->base = (unsigned long *)malloc(MAXQSIZE * sizeof(unsigned long));
	if (!q->base) {
		printf("%s line %d, malloc queue failure.\n",
		       __func__, __LINE__);
		exit(-1);
	}

	memset(q->base, 0x00, sizeof(unsigned long) * MAXQSIZE);
	q->front = q->rear = 0;

	return 0;
}

int queue_length(user_queue_t *q)
{
	return (q->rear - q->front + MAXQSIZE) % MAXQSIZE;
}

int enqueue(user_queue_t *q, unsigned long e)
{
	if ((q->rear + 1) % MAXQSIZE == q->front) {
		printf("%s line %d, queue full.\n",
		       __func__, __LINE__);
		return -1;
	}

	q->base[q->rear] = e;
	q->rear = (q->rear + 1) % MAXQSIZE;

	return 0;
}

unsigned long dequeue(user_queue_t *q)
{
	unsigned long e;

	if (q->front == q->rear) {
		printf("%s line %d, queue is null.\n",
		       __func__, __LINE__);
		return -1;
	}

	e = q->base[q->front];
	q->front = (q->front + 1) % MAXQSIZE;

	return e;
}

int main(void)
{
	int i;
	unsigned long e;

	init_queue(&uqueue);
	printf("%s line %d, queue_length %d.\n", __func__, __LINE__, queue_length(&uqueue));
	enqueue(&uqueue, 100);
	printf("%s line %d, queue_length %d.\n", __func__, __LINE__, queue_length(&uqueue));
	e = dequeue(&uqueue);
	printf("%s line %d, queue_length %d, e %ld.\n", __func__, __LINE__, queue_length(&uqueue), e);

	for (i = 0; i < MAXQSIZE; i ++) {
		enqueue(&uqueue, i);
		printf("%s line %d, queue_length %d.\n", __func__, __LINE__, queue_length(&uqueue));
	}

	for (i = 0; i < MAXQSIZE; i ++) {
		e = dequeue(&uqueue);
		printf("%s line %d, queue_length %d, e %ld.\n", __func__, __LINE__, queue_length(&uqueue), e);
	}

	return 0;
}

除了上面使用额外的mirror标志来提高循环队列的利用率之外,还有另外一种方法,就是使用单调递增的read index/write index,而不让其回绕,代价就是在需要获取读写位置的时候,需要对read/write index进行取模运算,参考如下修改:https://gitee.com/tugouxp/ringbuf/blob/master/ringbufnew.c

一个实际的使用例子是在corundum网卡实现发送队列中,参考如下项目中的实现:

git clone https://github.com/corundum/corundum 

doorbell 机制

中断是硬件给软件的doorbell, doorbell是软件给硬件的中断,下面是用C实现的一个doorbell的MODEL, 基于ring buffer,可以参考:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#define MAXQSIZE 1024
typedef struct {
	long *base;
	int front;
	int rear;
} user_queue_t;

typedef struct {
	long *base;
	int front;
	int rear;
} hwrb_queue_t;

static user_queue_t sw_queue;
static hwrb_queue_t hw_queue;
static pthread_mutex_t rbmutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t dbmutex = PTHREAD_MUTEX_INITIALIZER;

int init_queue_sw(user_queue_t *q, void *qbuf)
{
	q->base = qbuf;
	q->front = q->rear = 0;

	return 0;
}

int init_queue_hw(hwrb_queue_t *q, void *qbuf)
{
	q->base = qbuf;
	q->front = q->rear = 0;

	return 0;
}

int queue_length_sw(user_queue_t *q)
{
	int ret;

	pthread_mutex_lock(&rbmutex);
	ret = (q->rear - q->front + MAXQSIZE) % MAXQSIZE;
	pthread_mutex_unlock(&rbmutex);

	return ret;
}

int queue_length_hw(hwrb_queue_t *q)
{
	int ret;

	pthread_mutex_lock(&rbmutex);
	ret = (q->rear - q->front + MAXQSIZE) % MAXQSIZE;
	pthread_mutex_unlock(&rbmutex);

	return ret;
}

int enqueue(user_queue_t *q, long e)
{
	pthread_mutex_lock(&rbmutex);
	if ((q->rear + 1) % MAXQSIZE == q->front) {
		printf("%s line %d, queue full.\n",
		       __func__, __LINE__);
		pthread_mutex_unlock(&rbmutex);
		return -1;
	}

	q->base[q->rear] = e;
	q->rear = (q->rear + 1) % MAXQSIZE;
	pthread_mutex_unlock(&rbmutex);

	return 0;
}

long dequeue(user_queue_t *q)
{
	long e;

	pthread_mutex_lock(&rbmutex);
	if (q->front == q->rear) {
		printf("%s line %d, queue is null.\n",
		       __func__, __LINE__);
		pthread_mutex_unlock(&rbmutex);
		return -1;
	}

	e = q->base[q->front];
	q->front = (q->front + 1) % MAXQSIZE;
	pthread_mutex_unlock(&rbmutex);

	return e;
}

static void ring_doorbell(unsigned long dbval)
{
	pthread_mutex_lock(&dbmutex);
	hw_queue.rear = dbval;
	pthread_mutex_unlock(&dbmutex);
}

static void send_interupt(unsigned long itval)
{
	pthread_mutex_lock(&dbmutex);
	sw_queue.front = itval;
	pthread_mutex_unlock(&dbmutex);
}

static void *hw_reader_thread(void *args)
{
	int ret;
	long e = 0, next = 0;
	hwrb_queue_t *hwqueue = (hwrb_queue_t *)args;

	while (1) {
		ret = 0;
		pthread_mutex_lock(&rbmutex);
		if (hwqueue->front == hwqueue->rear) {
			printf("%s line %d, queue is empty.\n",
			       __func__, __LINE__);
			ret = -1;
		} else {
			e = hwqueue->base[hwqueue->front];
			hwqueue->front = (hwqueue->front + 1) % MAXQSIZE;
			send_interupt(hwqueue->front);
		}
		pthread_mutex_unlock(&rbmutex);

		if (ret == 0) {
			printf("%s line %d, queue_length %d, e %ld.\n",
			       __func__, __LINE__, queue_length_hw(hwqueue), e);
			if (next != 0) {
				if (e != next) {
					printf("%s line %d, val is not continue %ld, %ld.\n",
					       __func__, __LINE__, e, next);
					while (1);
				}
			}
			next = e + 1;
		}
	}

	return NULL;
}

void *sw_writer_thread(void *args)
{
	int ret;
	long e = 0;
	user_queue_t *swqueue = (user_queue_t *)args;

	while (1) {
		ret = 0;
		pthread_mutex_lock(&rbmutex);
		if ((swqueue->rear + 1) % MAXQSIZE == swqueue->front) {
			printf("%s line %d, queue is full.\n",
			       __func__, __LINE__);
			ret = -1;
		} else {
			swqueue->base[swqueue->rear] = e;
			swqueue->rear = (swqueue->rear + 1) % MAXQSIZE;
			ring_doorbell(swqueue->rear);
		}
		pthread_mutex_unlock(&rbmutex);

		if (ret == 0) {
			printf("%s line %d, queue_length %d enqueue %ld.\n",
			       __func__, __LINE__, queue_length_sw(swqueue), e);
			e ++;
#if 0
			// test continue of hw read.
			if (e == 100)
				e = 101;
#endif
		}
	}

	return NULL;
}

int main(void)
{
	int ret;
	void *qbuf;
	pthread_t hw_thread;
	pthread_t sw_thread;

	qbuf = malloc(MAXQSIZE * sizeof(unsigned long));
	if (!qbuf) {
		printf("%s line %d, malloc queue failure.\n",
		       __func__, __LINE__);
		exit(-1);
	}

	memset(qbuf, 0x00, sizeof(unsigned long) * MAXQSIZE);

	init_queue_sw(&sw_queue, qbuf);
	init_queue_hw(&hw_queue, qbuf);

	ret = pthread_create(&hw_thread, NULL, hw_reader_thread, &hw_queue);
	if (ret) {
		printf("error, return code is %d\n", ret);
		return -1;
	}

	ret = pthread_create(&sw_thread, NULL, sw_writer_thread, &sw_queue);
	if (ret) {
		printf("error, return code is %d\n", ret);
		return -1;
	}

	pthread_join(hw_thread, NULL);
	pthread_join(sw_thread, NULL);

	return 0;
}

结束!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值