关于kfifo
kfifo是内核里面的一个First In First Out数据结构,它采用环形循环队列的数据结构来实现;它提供一个无边界的字节流服务,最重要的一点是,它使用并行无锁编程技术,即当它用于只有一个入队线程和一个出队线程的场情时,两个线程可以并发操作,而不需要任何加锁行为,就可以保证kfifo的线程安全。
具体什么是环形缓冲区,请看我以前的文章
说明
关于kfifo的相关概念我不会介绍,有兴趣可以看他的相关文档,我只将其实现过程移植重写,移植到适用stm32开发板上,并且按照我个人习惯重新命名, RingBuff ->意为环形缓冲区
往期关于环形缓冲区的文章:
杰杰带你解读【机智云】环形缓冲区源码
STM32进阶之串口环形缓冲区实现
RingBuff_t
环形缓冲区的结构体成员变量,具体含义看注释。
buffer: 用于存放数据的缓存
size: buffer空间的大小
in, out: 和buffer一起构成一个循环队列。 in指向buffer中队头,而且out指向buffer中的队尾
typedef struct ringbuff
{
uint8_t *buffer; /* 数据区域 */
uint32_t size; /* 环形缓冲区大小 */
uint32_t in; /* 数据入队指针 (in % size) */
uint32_t out; /* 数据出队指针 (out % size) */
#if USE_MUTEX
MUTEX_T *mutex; /* 支持rtos的互斥 */
#endif
}RingBuff_t ;
Create_RingBuff
创建一个环形缓冲区,为了适应后续对缓冲区入队出队的高效操作,环形缓冲区的大小应为2^n字节,
如果不是这个大小,则系统默认裁剪以对应缓冲区字节。
当然还可以优化,不过我目前并未做,思路如下:如果系统支持动态分配内存,则向上对齐,避免浪费内存空间,否则就按照我默认的向下对齐,当内存越大,对齐导致内存泄漏则会越多。对齐采用的函数是roundup_pow_of_two。如果系统支持互斥量,那么还将创建一个互斥量用来做互斥访问,防止多线程同时使用导致数据丢失。
/************************************************************
* @brief Create_RingBuff
* @param rb:环形缓冲区句柄
* buffer:环形缓冲区的数据区域
* size:环形缓冲区的大小,缓冲区大小要为2^n
* @return err_t:ERR_OK表示创建成功,其他表示失败
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 用于创建一个环形缓冲区
***********************************************************/
err_t Create_RingBuff(RingBuff_t* rb,
uint8_t *buffer,
uint32_t size
)
{
if((rb == NULL)||(buffer == NULL)||(size == 0))
{
PRINT_ERR("data is null!");
return ERR_NULL;
}
PRINT_DEBUG("ringbuff size is %d!",size);
/* 缓冲区大小必须为2^n字节,系统会强制转换,
否则可能会导致指针访问非法地址。
空间大小越大,强转时丢失内存越多 */
if(size&(size - 1))
{
size = roundup_pow_of_two(size);
PRINT_DEBUG("change ringbuff size is %d!",size);
}
rb->buffer = buffer;
rb->size = size;
rb->in = rb->out = 0;
#if USE_MUTEX
/* 创建信号量不成功 */
if(!create_mutex(rb->mutex))
{
PRINT_ERR("create mutex fail!");
ASSERT(ASSERT_ERR);
return ERR_NOK;
}
#endif
PRINT_DEBUG("create ringBuff ok!");
return ERR_OK;
}
roundup_pow_of_two
/************************************************************
* @brief roundup_pow_of_two
* @param size:传递进来的数据长度
* @return size:返回处理之后的数据长度
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 用于处理数据,使数据长度必须为 2^n
* 如果不是,则转换,丢弃多余部分,如
* roundup_pow_of_two(66) -> 返回 64
***********************************************************/
static unsigned long roundup_pow_of_two(unsigned long x)
{
return (1 << (fls(x-1)-1)); //向下对齐
//return (1UL << fls(x - 1)); //向上对齐,用动态内存可用使用
}
Delete_RingBuff
删除一个环形缓冲区,删除之后,缓冲区真正存储地址是不会被改变的(目前我是使用自定义数组做缓冲区的),但是删除之后,就无法对缓冲区进行读写操作。并且如果支持os的话,创建的互斥量会被删除。
/************************************************************
* @brief Delete_RingBuff
* @param rb:环形缓冲区句柄
* @return err_t:ERR_OK表示成功,其他表示失败
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 删除一个环形缓冲区
***********************************************************/
err_t Delete_RingBuff(RingBuff_t *rb)
{
if(rb == NULL)
{
PRINT_ERR("ringbuff is null!");
return ERR_NULL;
}
rb->buffer = NULL;
rb->size = 0;
rb->in = rb->out = 0;
#if USE_MUTEX
if(!deleta_mutex(rb->mutex))
{
PRINT_DEBUG("deleta mutex is fail!");
return ERR_NOK;
}
#endif
return ERR_OK;
}
Write_RingBuff
向环形缓冲区写入指定数据,支持线程互斥访问。用户想要写入缓冲区的数据长度不一定是真正入队的长度,在完成的时候还要看看返回值是否与用户需要的长度一致~
这个函数很有意思,也是比较高效的入队操作,将指定区域的数据拷贝到指定的缓冲区中,过程看注释即可
/************************************************************
* @brief Write_RingBuff
* @param rb:环形缓冲区句柄
* @param wbuff:写入的数据起始地址
* @param len:写入数据的长度(字节)
* @return len:实际写入数据的长度(字节)
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 这个函数会从buff空间拷贝len字节长度的数据到
rb环形缓冲区中的空闲空间。
***********************************************************/
uint32_t Write_RingBuff(RingBuff_t *rb,
uint8_t *wbuff,
uint32_t len)
{
uint32_t l;
#if USE_MUTEX
/* 请求互斥量,成功才能进行ringbuff的访问 */
if(!request_mutex(rb->mutex))
{
PRINT_DEBUG("request mutex fail!");
return 0;
}
else /* 获取互斥量成功 */
{
#endif
len = min(len, rb->size - rb->in + rb->out);
/* 第一部分的拷贝:从环形缓冲区写入数据直至缓冲区最后一个地址 */
l = min(len, rb->size - (rb->in & (rb->size - 1)));
memcpy(rb->buffer + (rb->in & (rb->size - 1)), wbuff, l);
/* 如果溢出则在缓冲区头写入剩余的部分
如果没溢出这句代码相当于无效 */
memcpy(rb->buffer, wbuff + l, len - l);
rb->in += len;
PRINT_DEBUG("write ringBuff len is %d!",len);
#if USE_MUTEX
}
/* 释放互斥量 */
release_mutex(rb->mutex);
#endif
return len;
}
Read_RingBuff
读取缓冲区数据到指定区域,用户指定读取长度,用户想要读取的长度不一定是真正读取的长度,在读取完成的时候还要看看返回值是否与用户需要的长度一致~也支持多线程互斥访问。
也是缓冲区出队的高效操作。过程看代码注释即可
/************************************************************
* @brief Read_RingBuff
* @param rb:环形缓冲区句柄
* @param wbuff:读取数据保存的起始地址
* @param len:想要读取数据的长度(字节)
* @return len:实际读取数据的长度(字节)
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 这个函数会从rb环形缓冲区中的数据区域拷贝len字节
长度的数据到rbuff空间。
***********************************************************/
uint32_t Read_RingBuff(RingBuff_t *rb,
uint8_t *rbuff,
uint32_t len)
{
uint32_t l;
#if USE_MUTEX
/* 请求互斥量,成功才能进行ringbuff的访问 */
if(!request_mutex(rb->mutex))
{
PRINT_DEBUG("request mutex fail!");
return 0;
}
else
{
#endif
len = min(len, rb->in - rb->out);
/* 第一部分的拷贝:从环形缓冲区读取数据直至缓冲区最后一个 */
l = min(len, rb->size - (rb->out & (rb->size - 1)));
memcpy(rbuff, rb->buffer + (rb->out & (rb->size - 1)), l);
/* 如果溢出则在缓冲区头读取剩余的部分
如果没溢出这句代码相当于无效 */
memcpy(rbuff + l, rb->buffer, len - l);
rb->out += len;
PRINT_DEBUG("read ringBuff len is %d!",len);
#if USE_MUTEX
}
/* 释放互斥量 */
release_mutex(rb->mutex);
#endif
return len;
}
获取缓冲区信息
这些就比较简单了,看看缓冲区可读可写的数据有多少
/************************************************************
* @brief CanRead_RingBuff
* @param rb:环形缓冲区句柄
* @return uint32:可读数据长度 0 / len
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 可读数据长度
***********************************************************/
uint32_t CanRead_RingBuff(RingBuff_t *rb)
{
if(NULL == rb)
{
PRINT_ERR("ringbuff is null!");
return 0;
}
if(rb->in == rb->out)
return 0;
if(rb->in > rb->out)
return (rb->in - rb->out);
return (rb->size - (rb->out - rb->in));
}
/************************************************************
* @brief CanRead_RingBuff
* @param rb:环形缓冲区句柄
* @return uint32:可写数据长度 0 / len
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 可写数据长度
***********************************************************/