0


一种简单安全的消息队列的C语言解决方案

文章概要

〇、背景

基于嵌入式编程,对于有安全等级要求的项目,一般都会对编码有诸多安全性考虑的规则限制。在实际的编程中,消息队列的使用还是比较频繁的,但是对于不使用操作系统的情况下,消息队列相关的功能就需要手动实现。下面将会介绍一种简单的、安全等级较高的消息队列的实现方式。

一、基本思路

1.1 消息队列的属性要求

作为一个存储用的消息队列,一般情况下需要几个属性。

  • 消息队列的读指针:也就是数据从什么位置开始读取
  • 消息队列的写指针:也就是数据从什么位置开始写入
  • 消息队列的现存数据长度:也就是读取多少数据
  • 消息队列的空闲容量:也就是目前数据缓存区剩余的大小
  • 消息队列的已使用容量:也就是目前数据缓存区使用了的大小
  • 消息队列的容量:一般情况下,此容量表示的数据缓存区的大小
  • 消息队列数据缓冲区:也就是队列用于存放数据的地方

1.2 消息队列的结构定义

考虑到表示的精准性、使用的简洁性等方面,所以需要尽可能少的变量成员定义。本例中定义的结构如下。

/* 定义消息队列的数据类型 */typedefuint8_t QueueDataType_t;/* 定义消息队列的数据结构 */typedefstruct_message_queue_class_struct{
   uint32_t read_pos;/* 写队列数据指针 */uint32_t write_pos;/* 读队列数据指针 */uint32_t queue_size;/* 队列容量 */
    QueueDataType_t *queue_data;/* 队列数据 */} MsgQueueClass_st;

在如上的定义中,并没有直接定义 “消息队列的现存数据长度”“消息队列的空闲容量”“消息队列的已使用容量” 等信息变量,是因为上述信息均可以通过结构中定义的 “消息队列的读指针”“消息队列的写指针” 进行相关的运算获得,后面代码中会有相关的实现。

1.3 写入、读取数据的长度信息处理

通过上述的消息队列的数据结构定义,不难看出来成员 *

queue_data
  • 指向的是一个
    uint8_t
    
    的数据缓存区(数组),对于每一次写入操作,如何记录本次写入数据的长度其实很重要,因为本消息队列中存放的数据可能是通过多次写入的,而且每次写入的数据长度还不相同,那么在进行数据读取的时候,也不可能一下子读取缓存区中所有的数据,也不可能只读取一个随便长度的数据,这样的话整个队列管理起来就很困难。

所以为了方便消息队列的管理,设定每一次读取数据的长度即为此数据写入的时候的长度。

对于写入数据的长度信息的记录,比较方便记录和获取的方式就是写入到实际数据的头部,其占用的字节数可以自定义,本例中采用最常用的

uint16_t

来标识。所以:

  • 每次在消息队列中写入的数据时候,先在队列中写入本次数据的长度,然后再写入数据。
  • 每次在消息队列中读取的数据时候,先在队列中读取本次数据的长度,然后再读取目标长度数据。

二、基本实现

2.1 消息队列的初始化

消息队列的初始化,主要就是初始化读指针、写指针、绑定数据缓冲区、初始化缓冲区容量等基础属性的设置。其实现代码也比较简单。

/*
 *  @fn             MsgQueue_Init
 *  @brief          初始化队列,队列结构体首地址,队列数据类型,函数将初始化队列数据为初始化值状态
 *  @param[in]      queue_handle       队列结构体首地址
 *  @param[in]      queue_buffer       队列数据类型
 *  @param[in]      queue_size         队列数据容量
 *  @return         true 初始化成功  false 初始化失败
 */
bool MsgQueue_Init(MsgQueueClass_st *queue_handle, QueueDataType_t *queue_buffer,uint32_t queue_size){
   /** 参数判断 */if((NULL== queue_handle)||(0U== queue_size)||(NULL== queue_buffer)){
   return false;}memset(queue_buffer,0U, queue_size);/** 初始化队列数据 */
    queue_handle->read_pos =0U;
    queue_handle->write_pos =0U;
    queue_handle->queue_size = queue_size;
    queue_handle->queue_data = queue_buffer;/** 返回 */return true;}

2.2 消息队列的写入操作

2.2.1 消息队列的基础写入操作

消息队列数据的写入操作,需要考虑几个方面的问题:

  • 当前空闲容量是否足够
  • 当前写入数据位置是否需要翻转(也就是写入数据指针位置 + 写入数据的长度 是否 超过缓存区末尾)
  • 当前写入数据指针是否需要翻转

综上所述,消息队列的写入的基础操作,编码如下。

/*
 *  @fn         MsgQueue_WriteData
 *  @brief      队列写操作,输入队列结构体首地址、待写入数据和长度,函数将数据存在队列中
 *  @param[in]  queue_handle        队列结构体首地址
 *  @param[in]  write_buffer        待写入队列的数据
 *  @param[in]  write_data_len      待写入队列的数据长度
 *  @return     NONE
 *  @exception  此函数对输入指针参数未判断是否为NULL,如果任一指针参数为NULL,则可能引发core dump的异常。所以需要调用者保证指针参数的有效性
 */staticvoidMsgQueue_WriteData(MsgQueueClass_st *queue_handle,const QueueDataType_t *write_buffer,uint32_t write_data_len){
   registeruint32_t i, j;register QueueDataType_t *t_in_ptr;/* 由于已经能够判定有足够的空间,不需要再对 read_pos 值进行判断 */
    j = queue_handle->queue_size -(queue_handle->write_pos);
    t_in_ptr =&(queue_handle->queue_data[queue_handle->write_pos]);if(j > write_data_len){
   /* 写入时可以不用翻转 *//* 用自加指令可以明显提高指令的执行速度 */for(i =0U; i < write_data_len; i++){
   *t_in_ptr =*write_buffer;
            t_in_ptr++;
            write_buffer++;}
        queue_handle->write_pos += write_data_len;}else{
   /* 写入过程中必然翻转 *//* 将数据从当前指针到缓冲区尾部,写入的长度为j */for(i =0U; i < j; i++){
   *t_in_ptr =*write_buffer;
            t_in_ptr++;
            write_buffer++;

本文转载自: https://blog.csdn.net/zhemingbuhao/article/details/140840679
版权归原作者 青椒*^_^*凤爪爪 所有, 如有侵权,请联系我们删除。

“一种简单安全的消息队列的C语言解决方案”的评论:

还没有评论