VxWorks的环形缓冲区设计

VxWorks环形缓冲模块主要定义在rngLib.c和rngLib.h中,对于数据结构比较了解的小伙伴应该知道,环形缓冲实际就是一个双向循环队列。

概述

关于循环队列,小编之前在《也没想象中那么神秘的数据结构-先来后到的"队列"(循环队列)》一文中有过详细阐述,这里就不在过多的赘述了。

开发环境: vxWorks6.9.4,workbench3.3.5 开发板: TLZ7x-EasyEVM-A3

另外,小编所有文章均是自己亲手编写验证,若需要小编的工程代码,访问地址 实时系统vxWorks - 环形缓冲工程文件 获取。

文件内容如下:

  • obj: 存放目标文件,包含vxWorks镜像,应用程序目标文件。

  • rng_test: vxWorks应用工程。

接口

官方接口

官方环形缓冲接口定义主要包含在rngLib.h头文件中。

环形缓冲接口定义

        typedef struct		/* RING - ring buffer */
        {
            size_t pToBuf;	/* 写指针 */
            size_t pFromBuf;	/* 读指针 */
            size_t bufSize;	/* 数据长度 */
            char *buf;		/* 存放数据 */
        } RING;
         
        /* END_HIDDEN */
        typedef RING *RING_ID;

创建/删除环形缓冲

        /**
         * @创建环形缓冲
         * @nbytes: 缓冲尺寸
         * @成功返回环形缓冲ID,失败返回NULL。
         */
        extern RING_ID rngCreate (size_t nbytes);
         
        /**
         * @删除环形缓冲
         * @ringId: 环形缓冲ID   
         */
        extern void rngDelete (RING_ID ringId);

判断缓冲是否空/满

        /**
         * @判断环形缓冲是否为空
         * @ringId: 环形缓冲ID   
         * @不为空返回0,否则返回其它。
         */
        extern BOOL rngIsEmpty (RING_ID ringId);
         
        /**
         * @判断环形缓冲是否为满
         * @ringId: 环形缓冲ID   
         * @不为满返回0,否则返回其它。
         */
        extern BOOL rngIsFull (RING_ID ringId);

读写数据

        /**
         * @从环形缓冲中读取数据
         * @ringId: 环形缓冲ID         buffer:数据         maxbytes:长度
         * @返回实际读取到的数据长度。
         */
        extern size_t rngBufGet (RING_ID rngId, char *buffer, size_t maxbytes);
         
        /**
         * @写数据到环形缓冲
         * @ringId: 环形缓冲ID         buffer:数据         nbytes:长度  
         * @返回实际写入数据长度
         */
        extern size_t rngBufPut (RING_ID rngId, char *buffer, size_t nbytes);

获取缓冲区剩余空间/已占空间

        /**
         * @获取环形缓冲剩余空间长度
         * @ringId: 环形缓冲ID 
         * @返回剩余空间长度。
         */
        extern size_t     rngFreeBytes (RING_ID ringId);
         
        /**
         * @获取环形缓冲已占空间长度
         * @ringId: 环形缓冲ID
         * @返回已占空间长度。
         */
        extern size_t     rngNBytes (RING_ID ringId);

清空缓冲

        /**
         * @清空环形缓冲
         * @ringId: 环形缓冲ID
         */
        extern void rngFlush (RING_ID ringId);

环形缓冲对象接口

属性定义

        /* 环形缓冲类 */
        struct t_rng {
            u8_t            flag;   /* 创建标志,=1已创建,=0未创建 */
            RING_ID       id;      /* 环形缓冲 */ 
            struct t_sem    msem;   /* 环形缓冲互斥信号量 */ 
        };

保存数据到环形缓冲

        /**
         * @保存数据到环形缓冲
         * @p_rng:环形缓冲类   buf: 数据   len:长度
         **/
        void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);

获取环形缓冲数据

        /**
         * @获取环形缓冲数据
         * @p_rng:环形缓冲类    buf装载数据, len数据长度
         * 成功返回实际数据长度,失败返回ERROR
         **/
        s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);

创建环形缓冲

        /**
         * @创建环形缓冲
         * @p_rng:环形缓冲类   nbyte申请缓冲大小, name互斥信号名
         **/
        void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name);

示例

  • 示例创建两个任务(生产者/消费者),生产者向缓冲写入数据,消费者从缓冲读取数据,缓冲访问时使用互斥信号量进行互斥。

  • 关于信号量和任务部分可参加小编的《实时系统vxWorks - 信号量(重要)》和《实时系统vxWorks - 任务(重要)》文章。

  • 包含环形缓冲类rng.c/rng.h和演示程序main.c(已验证通过)。

rng.h

        /**
         * @Filename : rng.h
         * @Revision : $Revision: 1.00 $
         * @Author : Feng
         * @Description : 环形缓冲类,在系统自带环形缓冲基础上封装互斥信号量
        **/
         
        #ifndef __RNG_CLASS_H__
        #define __RNG_CLASS_H__
         
        #include   
        #include 
        #include 
        #include 
        #include 
        #include "sem.h"
         
        extern struct sem sem; /* 保存系统信号量信息 */
         
        /* 环形缓冲类 */
        struct t_rng {
            u8_t            flag;   /* 创建标志,=1已创建,=0未创建 */
            RING_ID       id;      /* 环形缓冲 */ 
            struct t_sem    msem;   /* 环形缓冲互斥信号量 */ 
        };
         
        /**
         * @保存数据到环形缓冲
         * @p_rng:环形缓冲类   buf: 数据   len:长度
         **/
        void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
         
        /**
         * @获取环形缓冲数据
         * @p_rng:环形缓冲类    buf装载数据, len数据长度
         * 成功返回实际数据长度,失败返回ERROR
         **/
        s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
         
        /**
         * @创建环形缓冲
         * @p_rng:环形缓冲类   nbyte申请缓冲大小, name互斥信号名
         **/
        void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name);
         
        #endif

rng.c

        /**
         * @Filename : rng.c
         * @Revision : $Revision: 1.00 $
         * @Author : Feng
         * @Description : 环形缓冲类,在系统自带环形缓冲基础上封装互斥信号量
        **/
         
        #include "rng.h"
         
        /**
         * @保存数据到环形缓冲
         * @p_rng:环形缓冲类   buf: 数据   len:长度
         **/
        void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len)
        {  
            if (p_rng->flag == 0)
                return;
         
            get_sem(&p_rng->msem);                  
            if (!(rngIsFull(p_rng->id)))
                rngBufPut(p_rng->id, buf, len); 
            lose_sem(&p_rng->msem); 
        }
         
        /**
         * @获取环形缓冲数据
         * buf装载数据, len数据长度
         * 成功返回实际数据长度,失败返回ERROR
         **/
        s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len)
        {  
            s32_t rLen = ERROR;
         
            if (p_rng->flag == 0)
                return ERROR;
         
            get_sem(&p_rng->msem);                  
            if (!(rngIsEmpty(p_rng->id)))
                rLen = rngBufGet(p_rng->id, buf, len);
            lose_sem(&p_rng->msem); 
         
            return (rLen);
        }
         
        /**
         * @创建环形缓冲
         * nbyte申请缓冲大小, name互斥信号名
         **/
        void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name)
        {
            if (p_rng->flag == 1)
                return;
         
            p_rng->id = rngCreate(nbytes);
         
            p_rng->msem.sem = NULL;
            p_rng->msem.type = SEM_MUTEX;   
            strcpy(p_rng->msem.name, name);
            resgister_sem(&sem, &p_rng->msem);  
         
            p_rng->flag = 1;
        }

main.c

        /**
         * @Filename : main.c
         * @Revision : $Revision: 1.00 $
         * @Author : Feng
         * @Description : 环形缓冲类使用示例
        **/
         
        #include      
        #include     
        #include "stdioLib.h"     
        #include "strLib.h"     
        #include "task.h"
        #include "sem.h"
        #include "rng.h"
        #include "feng_type.h"
         
        #define RNG_SIZE    100 /* 缓冲大小 */
        struct sem     sem; /* 保存系统信号量信息 */
        struct t_task s_task, s_task1;
        struct t_sem s_sem, s_sem1;
        struct t_rng rng;
         
        /**
         * @生产者:定时存储数据到环形缓冲
        **/
        static void _thread(void)
        {  
            static int cnt = 77;
         
            while (1) { 
                get_sem(&s_sem);        
                put_rng(&rng, &cnt, sizeof(int));       
                printf("put data : %d...\n", cnt++);
            }
        }
         
        /**
         * @消费者:定时从环形缓冲中取出数据
        **/
        static void _thread1(void)
        {  
            int cnt1 = 0;
         
            while (1) { 
                get_sem(&s_sem1);
         
                if (get_rng(&rng, &cnt1, sizeof(int)) > 0)
                    printf("get data : %d...\n", cnt1);
            }
        }
         
        /**
         * @创建任务
         * @p_task:任务类    name:任务名        thread:函数
        **/
        void _create_task(struct t_task *p_task, char *name, FUNCPTR thread)
        {
            strcpy(p_task->name, name);     
            p_task->options    = VX_FP_TASK;
            p_task->stackSize  = 50 * 1024;
            p_task->pFunc      = thread;
            p_task->tid        = NULL;
            p_task->core       = 0;
            p_task->priority   = 102;
         
            create_task(p_task);
            start_task(p_task);
        }
         
        /**
         * @创建信号量
         * @p_sem:信号量类        name:信号量名       type:类型
        **/
        void _create_sem(struct t_sem *p_sem, char *name, E_SEM_TYPE type)
        {
            strcpy(p_sem->name, name);      
            p_sem->period = 100;
            p_sem->type = type;
            p_sem->sem = SEM_ID_NULL;
         
            resgister_sem(&sem, p_sem);
        }
         
         
        int main(void)
        {
            sysClkRateSet(100);         /* 时间片设置 */
         
            create_rng(&rng, RNG_SIZE, "my_rng");
         
            _create_sem(&s_sem, "my_sem", SEM_BIN);
            _create_sem(&s_sem1, "my_sem1", SEM_BIN);
         
            _create_task(&s_task, "my_task", (FUNCPTR)_thread);
            _create_task(&s_task1, "my_task1", (FUNCPTR)_thread1);
         
            while (1) { 
                taskDelay(500);     /* 5s */
                lose_sem_by_name(&sem, "my_sem");
                lose_sem_by_name(&sem, "my_sem1");  
            }
         
            return 0;
        }

验证

使用环形缓冲之前需要先添加INCLUDE_RNG_BUF组件。

打开镜像工程,选择kernel Configuration。按住Ctrl+F,输入rng,找到ring buffers,添加组件。

vxworks ring buffer

添加完组件后,编译镜像,将镜像拷贝到目标机加载指定目录。

创建应用工程rng_test,输入相关测试代码,运行后如下图所示。

vxworks ring buffer

注意:若不知道工程如何创建以及运行,可参见小编文章《实时系统vxWorks - 任务(重要)》和《实时系统vxWorks - 加载应用程序的方法》。