仿照kfifo编写一个ring_buffer

来源:互联网 发布:青岛海关数据 编辑:程序博客网 时间:2024/06/05 05:52
/**@brief 仿照linux kfifo写的ring buffer *@atuher Anker  date:2013-12-18* ring_buffer.h * */#ifndef KFIFO_HEADER_H #define KFIFO_HEADER_H#include <inttypes.h>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <assert.h>//判断x是否是2的次方#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))//取a和b中最小值#define min(a, b) (((a) < (b)) ? (a) : (b))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 (!is_power_of_2(size))    {    fprintf(stderr,"size must be power of 2.\n");        return ring_buf;    }    ring_buf = (struct ring_buffer *)malloc(sizeof(struct ring_buffer));    if (!ring_buf)    {        fprintf(stderr,"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;}//释放缓冲区void ring_buffer_free(struct ring_buffer *ring_buf){    if (ring_buf)    {    if (ring_buf->buffer)    {        free(ring_buf->buffer);        ring_buf->buffer = NULL;    }    free(ring_buf);    ring_buf = NULL;    }}//缓冲区的长度uint32_t __ring_buffer_len(const struct ring_buffer *ring_buf){    return (ring_buf->in - ring_buf->out);}//从缓冲区中取数据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);            /* first get the data from fifo->out until the end of the buffer */    len = min(size, ring_buf->size - (ring_buf->out & (ring_buf->size - 1)));    memcpy(buffer, ring_buf->buffer + (ring_buf->out & (ring_buf->size - 1)), 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;    size = min(size, ring_buf->size - ring_buf->in + ring_buf->out);    /* first put the data starting from fifo->in to buffer end */    len  = min(size, ring_buf->size - (ring_buf->in & (ring_buf->size - 1)));    memcpy(ring_buf->buffer + (ring_buf->in & (ring_buf->size - 1)), 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;}uint32_t ring_buffer_len(const struct ring_buffer *ring_buf){    uint32_t len = 0;    pthread_mutex_lock(ring_buf->f_lock);    len = __ring_buffer_len(ring_buf);    pthread_mutex_unlock(ring_buf->f_lock);    return len;}uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size){    uint32_t ret;    pthread_mutex_lock(ring_buf->f_lock);    ret = __ring_buffer_get(ring_buf, buffer, size);    //buffer中没有数据    if (ring_buf->in == ring_buf->out)    ring_buf->in = ring_buf->out = 0;    pthread_mutex_unlock(ring_buf->f_lock);    return ret;}uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size){    uint32_t ret;    pthread_mutex_lock(ring_buf->f_lock);    ret = __ring_buffer_put(ring_buf, buffer, size);    pthread_mutex_unlock(ring_buf->f_lock);    return ret;}#endif

采用多线程模拟生产者和消费者编写测试程序,如下所示:

/**@brief ring buffer测试程序,创建两个线程,一个生产者,一个消费者。 * 生产者每隔1秒向buffer中投入数据,消费者每隔2秒去取数据。 *@atuher Anker  date:2013-12-18 * */#include "ring_buffer.h"#include <pthread.h>#include <time.h>#define BUFFER_SIZE  1024 * 1024typedef struct student_info{    uint64_t stu_id;    uint32_t age;    uint32_t score;}student_info;void print_student_info(const student_info *stu_info){    assert(stu_info);    printf("id:%lu\t",stu_info->stu_id);    printf("age:%u\t",stu_info->age);    printf("score:%u\n",stu_info->score);}student_info * get_student_info(time_t timer){    student_info *stu_info = (student_info *)malloc(sizeof(student_info));    if (!stu_info)    {    fprintf(stderr, "Failed to malloc memory.\n");    return NULL;    }    srand(timer);    stu_info->stu_id = 10000 + rand() % 9999;    stu_info->age = rand() % 30;    stu_info->score = rand() % 101;    print_student_info(stu_info);    return stu_info;}void * consumer_proc(void *arg){    struct ring_buffer *ring_buf = (struct ring_buffer *)arg;    student_info stu_info;     while(1)    {    sleep(2);    printf("------------------------------------------\n");    printf("get a student info from ring buffer.\n");    ring_buffer_get(ring_buf, (void *)&stu_info, sizeof(student_info));    printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));    print_student_info(&stu_info);    printf("------------------------------------------\n");    }    return (void *)ring_buf;}void * producer_proc(void *arg){    time_t cur_time;    struct ring_buffer *ring_buf = (struct ring_buffer *)arg;    while(1)    {    time(&cur_time);    srand(cur_time);    int seed = rand() % 11111;    printf("******************************************\n");    student_info *stu_info = get_student_info(cur_time + seed);    printf("put a student info to ring buffer.\n");    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));    printf("ring buffer length: %u\n", ring_buffer_len(ring_buf));    printf("******************************************\n");    sleep(1);    }    return (void *)ring_buf;}int consumer_thread(void *arg){    int err;    pthread_t tid;    err = pthread_create(&tid, NULL, consumer_proc, arg);    if (err != 0)    {    fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n",        errno, strerror(errno));    return -1;    }    return tid;}int producer_thread(void *arg){    int err;    pthread_t tid;    err = pthread_create(&tid, NULL, producer_proc, arg);    if (err != 0)    {    fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n",        errno, strerror(errno));    return -1;    }    return tid;}int main(){    void * buffer = NULL;    uint32_t size = 0;    struct ring_buffer *ring_buf = NULL;    pthread_t consume_pid, produce_pid;    pthread_mutex_t *f_lock = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));    if (pthread_mutex_init(f_lock, NULL) != 0)    {    fprintf(stderr, "Failed init mutex,errno:%u,reason:%s\n",        errno, strerror(errno));    return -1;    }    buffer = (void *)malloc(BUFFER_SIZE);    if (!buffer)    {    fprintf(stderr, "Failed to malloc memory.\n");    return -1;    }    size = BUFFER_SIZE;    ring_buf = ring_buffer_init(buffer, size, f_lock);    if (!ring_buf)    {    fprintf(stderr, "Failed to init ring buffer.\n");    return -1;    }#if 0    student_info *stu_info = get_student_info(638946124);    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));    stu_info = get_student_info(976686464);    ring_buffer_put(ring_buf, (void *)stu_info, sizeof(student_info));    ring_buffer_get(ring_buf, (void *)stu_info, sizeof(student_info));    print_student_info(stu_info);#endif    printf("multi thread test.......\n");    produce_pid  = producer_thread((void*)ring_buf);    consume_pid  = consumer_thread((void*)ring_buf);    pthread_join(produce_pid, NULL);    pthread_join(consume_pid, NULL);    ring_buffer_free(ring_buf);    free(f_lock);    return 0;}

总结:

len = min(len, fifo->size - fifo->in + fifo->out); 
      在 len 和 (fifo->size - fifo->in + fifo->out) 之间取一个较小的值赋给len。注意,当 (fifo->in == fifo->out+fifo->size) 时,表示缓冲区已满,此时得到的较小值一定是0,后面实际写入的字节数也全为0。
      另一种边界情况是当 len 很大时(因为len是无符号的,负数对它来说也是一个很大的正数),这一句也能保证len取到一个较小的值,因为    fifo->in总是大于等于 fifo->out ,所以后面的那个表达式 l = min(len, fifo->size - (fifo->in & (fifo->size - 1))); 的值不会超过fifo->size的大小。
      smp_mb();  smp_wmb(); 是加内存屏障,这里不是我们讨论的范围,你可以忽略它。 
      l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));    是把上一步决定的要写入的字节数len “切开”,这里又使用了一个技巧。注意:实际分配给fifo->buffer 的字节数 fifo->size,必须是2的幂,否则这里就会出错。既然 fifo->size 是2的幂,那么 (fifo->size-1) 也就是一个后面几位全为1的数,也就能保证(fifo->in & (fifo->size - 1)) 总为不超过 (fifo->size - 1) 的那一部分,和 (fifo->in)% (fifo->size - 1) 的效果一样。 
      这样后面的代码就不难理解了,它先向  fifo->in  到缓冲区末端这一块写数据,如果还没写完,在从缓冲区头开始写入剩下的,从而实现了循环缓冲。最后,把写指针后移 len 个字节,并返回len。
       从上面可以看出,fifo->in的值可以从0变化到超过fifo->size的数值,fifo->out也如此,但它们的差不会超过fifo->size。


0 0