基于Linux的kfifo移植到STM32(支持os的互斥访问)

基于Linux的kfifo移植到STM32(支持os的互斥访问)

声明:本文为杰杰原创,转载请说明出处 https://blog.csdn.net/jiejiemcu/article/details/82898387

关于kfifo

kfifo是内核里面的一个First In First Out数据结构,它采用环形循环队列的数据结构来实现;它提供一个无边界的字节流服务,最重要的一点是,它使用并行无锁编程技术,即当它用于只有一个入队线程和一个出队线程的场情时,两个线程可以并发操作,而不需要任何加锁行为,就可以保证kfifo的线程安全。

具体什么是环形缓冲区,请看我以前的文章

说明

关于kfifo的相关概念我不会介绍,有兴趣可以看他的相关文档,我只将其实现过程移植重写,移植到适用stm32开发板上,并且按照我个人习惯重新命名,RingBuff->意为环形缓冲区

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    可写数据长度
  ***********************************************************/
uint32_t CanWrite_RingBuff(RingBuff_t *rb)
{
	if(NULL == rb)
	{
		PRINT_ERR("ringbuff is null!");
		return 0;
	}

	return (rb->size - CanRead_RingBuff(rb));
}

附带

这里的代码我是用于测试的,随便写的

	RingBuff_t ringbuff_handle;
	
	uint8_t rb[64];
	uint8_t res[64];
	Create_RingBuff(&ringbuff_handle, 
								rb,
								sizeof(rb));
			Write_RingBuff(&ringbuff_handle,
                     res, 
                     datapack.data_length);
			
			PRINT_DEBUG("CanRead_RingBuff = %d!",CanRead_RingBuff(&ringbuff_handle));
			PRINT_DEBUG("CanWrite_RingBuff = %d!",CanWrite_RingBuff(&ringbuff_handle));
			
			Read_RingBuff(&ringbuff_handle,
                     res, 
                     datapack.data_length);

支持多个os的互斥量操作

此处模仿了文件系统的互斥操作

#if USE_MUTEX
#define  MUTEX_TIMEOUT   1000     /* 超时时间 */
#define  MUTEX_T         mutex_t  /* 互斥量控制块 */
#endif

/*********************************** mutex **************************************************/
#if USE_MUTEX
/************************************************************
  * @brief   create_mutex
  * @param   mutex:创建信号量句柄
  * @return  创建成功为1,0为不成功。
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    创建一个互斥量,用户在os中互斥使用ringbuff,
  *          支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
  ***********************************************************/
static err_t create_mutex(MUTEX_T *mutex)
{
  err_t ret = 0;

//	*mutex = rt_mutex_create("test_mux",RT_IPC_FLAG_PRIO); /* rtt */
//	ret = (err_t)(*mutex != RT_NULL);
	
//	*mutex = CreateMutex(NULL, FALSE, NULL);		/* Win32 */
//	ret = (err_t)(*mutex != INVALID_HANDLE_VALUE);

//	*mutex = OSMutexCreate(0, &err);		/* uC/OS-II */
//	ret = (err_t)(err == OS_NO_ERR);

//	*mutex = xSemaphoreCreateMutex();	/* FreeRTOS */
//	ret = (err_t)(*mutex != NULL);

//  ret = LOS_MuxCreate(&mutex);  /* LiteOS */
//	ret = (err_t)(ret != LOS_OK);
  return ret;
}
/************************************************************
  * @brief   deleta_mutex
  * @param   mutex:互斥量句柄
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    删除一个互斥量,支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
  ***********************************************************/
static err_t deleta_mutex(MUTEX_T *mutex)
{
	err_t ret;
	
//	ret = rt_mutex_delete(mutex);	/* rtt */
	
//	ret = CloseHandle(mutex);	/* Win32 */

//	OSMutexDel(mutex, OS_DEL_ALWAYS, &err);	/* uC/OS-II */
//	ret = (err_t)(err == OS_NO_ERR);

//  vSemaphoreDelete(mutex);		/* FreeRTOS */
//	ret = 1;

//  ret = LOS_MuxDelete(&mutex);  /* LiteOS */
//	ret = (err_t)(ret != LOS_OK);

	return ret;
}
/************************************************************
  * @brief   request_mutex
  * @param   mutex:互斥量句柄
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    请求一个互斥量,得到互斥量的线程才允许进行访问缓冲区
  *          支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
  ***********************************************************/
static err_t request_mutex(MUTEX_T *mutex)
{
	err_t ret;

//	ret = (err_t)(rt_mutex_take(mutex, MUTEX_TIMEOUT) == RT_EOK);/* rtt */
	
//	ret = (err_t)(WaitForSingleObject(mutex, MUTEX_TIMEOUT) == WAIT_OBJECT_0);	/* Win32 */

//	OSMutexPend(mutex, MUTEX_TIMEOUT, &err));		/* uC/OS-II */
//	ret = (err_t)(err == OS_NO_ERR);

//	ret = (err_t)(xSemaphoreTake(mutex, MUTEX_TIMEOUT) == pdTRUE);	/* FreeRTOS */

//  ret = (err_t)(LOS_MuxPend(mutex,MUTEX_TIMEOUT) == LOS_OK);  		/* LiteOS */

	return ret;
}
/************************************************************
  * @brief   release_mutex
  * @param   mutex:互斥量句柄
  * @return  NULL
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    释放互斥量,当线程使用完资源必须释放互斥量
  *          支持的os有rtt、win32、ucos、FreeRTOS、LiteOS
  ***********************************************************/
static void release_mutex(MUTEX_T *mutex)
{
//	rt_mutex_release(mutex);/* rtt */
	
//	ReleaseMutex(mutex);		/* Win32 */

//	OSMutexPost(mutex);		/* uC/OS-II */

//	xSemaphoreGive(mutex);	/* FreeRTOS */

//  LOS_MuxPost(mutex);   /* LiteOS */
}
#endif
/*********************************** mutex **************************************************/

debug.h

最后送一份debug的简便操作源码,因为前文很多时候会调用
PRINT_ERR
PRINT_DEBUG

#ifndef _DEBUG_H
#define _DEBUG_H
/************************************************************
  * @brief   debug.h
  * @author  jiejie
  * @github  https://github.com/jiejieTop
  * @date    2018-xx-xx
  * @version v1.0
  * @note    此文件用于打印日志信息
  ***********************************************************/
/**
* @name Debug print 
* @{
*/
#define PRINT_DEBUG_ENABLE		1		/* 打印调试信息 */
#define PRINT_ERR_ENABLE			1 	/* 打印错误信息 */
#define PRINT_INFO_ENABLE			0		/* 打印个人信息 */


#if PRINT_DEBUG_ENABLE
#define PRINT_DEBUG(fmt, args...) 	 do{(printf("\n[DEBUG] >> "), printf(fmt, ##args));}while(0)     
#else
#define PRINT_DEBUG(fmt, args...)	     
#endif

#if PRINT_ERR_ENABLE
#define PRINT_ERR(fmt, args...) 	 do{(printf("\n[ERR] >> "), printf(fmt, ##args));}while(0)     
#else
#define PRINT_ERR(fmt, args...)	       
#endif

#if PRINT_INFO_ENABLE
#define PRINT_INFO(fmt, args...) 	 do{(printf("\n[INFO] >> "), printf(fmt, ##args));}while(0)     
#else
#define PRINT_INFO(fmt, args...)	       
#endif

/**@} */
	
//针对不同的编译器调用不同的stdint.h文件
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
    #include <stdint.h>
#endif

/* 断言 Assert */
#define AssertCalled(char,int) 	printf("\nError:%s,%d\r\n",char,int)
#define ASSERT(x)   if((x)==0)  AssertCalled(__FILE__,__LINE__)
  
typedef enum 
{
	ASSERT_ERR = 0,								/* 错误 */
	ASSERT_SUCCESS = !ASSERT_ERR	/* 正确 */
} Assert_ErrorStatus;

typedef enum 
{
	FALSE = 0,		/* 假 */
	TRUE = !FALSE	/* 真 */
}ResultStatus;

#endif /* __DEBUG_H */
### 回答1: kfifoLinux内核中一种用于缓冲数据的数据结构,用于协调生产者和消费者的操作。它通过环形队列的方式管理数据的读写,实现了高效的数据传输。 将kfifo移植到应用层,可以实现在用户空间中也能利用kfifo提供的高效数据缓冲功能。 首先,我们需要在应用程序中引入kfifo的头文件,并将其编译进应用程序的代码中。 然后,我们可以通过以下步骤使用kfifo: 1. 初始化kfifo:使用kfifo_init函数进行初始化,指定缓冲区大小和数据类型。 2. 写入数据:使用kfifo_in函数将需要传输的数据写入到kfifo缓冲区中。 3. 读取数据:使用kfifo_out函数从kfifo缓冲区中读取需要的数据。 4. 判断缓冲区状态:使用kfifo_is_empty和kfifo_is_full函数可以判断缓冲区是否为空或已满。 通过移植kfifo到应用层,我们可以在用户空间中更灵活地使用这个高效的数据结构。我们可以在应用程序中使用kfifo来管理数据缓冲区,实现生产者和消费者之间的数据传输,并且可以根据需求进行扩展和修改。 总结起来,将kfifo移植到应用层,可以提供一个高效的缓冲区管理方式,有助于提高应用程序的数据传输效率和性能。 ### 回答2: kfifoLinux内核中用于实现无锁队列的一种数据结构。它具有高效、可靠和线程安全等优点,因此在应用层进行移植时也能发挥其特性。 首先,应用层移植kfifo需要将kfifo的源代码从内核中提取出来,并进行相应的修改,以适应应用层的环境。这包括调整数据结构的定义和函数的接口,确保其与应用层代码的兼容性。 其次,在移植kfifo时,需要根据具体的应用场景和需求来调整kfifo的大小和配置。kfifo的大小应根据实际数据量进行设置,以充分利用内存资源并避免溢出或浪费。同时,还可以根据需要调整kfifo是否支持多线程并发操作。 在应用层中使用kfifo时,可以通过调用kfifo提供的接口来实现队列的入队和出队操作。这包括向kfifo中插入元素、从kfifo中获取元素以及获取队列的状态等。同时,还可以根据需要进行扩展,以满足具体应用场景的要求。 移植kfifo到应用层还需要注意线程安全性和并发控制。由于应用层可能存在多个线程同时对kfifo进行读写操作,因此需要增加适当的同步机制,如锁或原子操作,以确保数据的一致性和线程的安全。 总之,kfifo移植到应用层可以提供高效、可靠和线程安全的队列功能,适用于各种应用场景。但在移植过程中需要注意调整数据结构、接口和配置,并根据具体需求增加适当的扩展和并发控制机制,以提供更好的性能和可靠性。 ### 回答3: kfifoLinux内核中的一个环形缓冲区实现,通常用于在内核中进行高效的数据传输。要将kfifo移植到应用层,需要进行以下步骤: 1. 了解kfifo的基本原理:kfifo是一个循环队列,具有读写指针和缓冲区。数据按照FIFO(先进先出)原则在缓冲区中传输。 2. 将kfifo相关的头文件和源代码复制到应用程序的源代码目录中。 3. 修改kfifo源代码中与内核相关的部分,使其适应用户空间。这可能包括对内存分配函数和同步原语的更改。 4. 修改kfifo的接口,使其适应应用层的数据传输需求。对于内核中的kfifo,其接口可能是以IO操作的形式提供的,而在应用层中,可以使用API函数来替代。 5. 在应用层中,使用相应的API函数来初始化kfifo对象,并进行读写操作。注意,在应用层中需要自己实现同步机制来保证多线程环境下的数据一致性。 6. 根据应用程序的需求,对kfifo进行适当的修改和扩展。例如,可以添加自定义的数据类型,修改缓冲区大小,以及添加其他辅助函数。 7. 进行测试和调试,确保kfifo在应用层中能够正常工作。可以编写相应的测试代码来验证kfifo的正确性和性能。 总结起来,将kfifo移植到应用层需要了解kfifo的原理和接口,进行必要的修改和调整,并进行测试和调试以保证其正常工作。移植后的kfifo可以在应用层中用于高效的数据传输和缓冲处理。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值