队列结构、内存管理与QP原生事件队列、QP原生事件池

5 篇文章 0 订阅
2 篇文章 0 订阅

目录

发布事件与取出、处理事件的简要说明

原生事件队列

原生事件队列初始化

使用MyQueue_post_FIFO发送事件

使用MyQueue_get函数获取一个事件

队列使用情况简图

QP原生事件池/内存池

事件池的初始化

从池里获得一个内存块

把一个内存块回收到池内

VS测试代码

队列

事件池

头文件

主函数


发布事件与取出、处理事件的简要说明

发布事件

  1. 从内存池获取一块空闲内存,返回一个首地址,并将其强制转化为MyEvt类型,当做事件e。
  2. 将e赋予初始值。事件信号、参数等。
  3. 调用发布事件函数,插入到事件队列中去。.

取出事件

  1. 从事件队列里取出一个事件e,并处理。
  2. 将事件e作为指针,回收到内存池。

 

原生事件队列

一个活动对象有(QHsm类型)的super派生出队列eQueue、优先级priority等。而队列结构体QEQueue中的**ring指向MyQueueSto。这是一个指针数组,数组内的元素是指向事件池某个地址的指针。

所以事件队列内的事件,其实是内存池的某块内存的首地址。

所有离开的事件必须通过 frontEvt 数据成员传递。这个在环形缓存的额外的位置通过允许频繁的绕过缓存操作优化了队列操作,因为在空和非空状态之间非常频繁的队列改变时,每次仅一个事件出现在队列。另外, frontEvt 指针也被用作一个队列状态指示器, frontEvt的值为 NULL 指示队列为空。索引 head , tail ,和 end 和 ring 指针有关。事件总是从缓存的 tail 索引处被提取,新的事件典型地是在 head索引处被插入。在 head插入事件,在 tail 处提取事件,对应于FIFO队列( postFIFO() 操作)。当事件被提取时, tail 总是递减,同样在一个事件被插入时head索引也是如此。索引值 0 限制了 head 和 tail 索引的范围,它们在达到 0 时必须“绕回” end 。

typedef struct QActive {
    QHsm super; 
    QF_EQUEUE_TYPE eQueue; // 活动对象的事件
    QF_OS_OBJECT_TYPE osObject;
    QF_THREAD_TYPE thread;
    uint8_t prio;
    uint8_t startPrio;
} QActive;

typedef struct QEQueue {
    QEvt const * volatile frontEvt;
    QEvt const **ring;
    QEQueueCtr end;
    QEQueueCtr volatile head;
    QEQueueCtr volatile tail;
    QEQueueCtr volatile nFree;
    QEQueueCtr nMin;
} QEQueue;

原生事件队列初始化

int Queue_int(MyQueue * const me, MyEvt const *qSto[], uint_fast16_t const qLen)
{
	me->frontEvt = (MyEvt const *)0; /* no events in the queue */
	me->ring = &qSto[0];        /* the beginning of the ring buffer */
	me->end = (uint_fast16_t)qLen;
	if (qLen != (uint_fast16_t)0) {
		me->head = (uint_fast16_t)0;
		me->tail = (uint_fast16_t)0;
	}
	me->nFree = (uint_fast16_t)(qLen + (uint_fast16_t)1); /*+1 for frontEvt */
	me->nMin = me->nFree;
	return 0;
}
  1. 外部位置frontEvt置0,当前没有事件。
  2. ring指向事件队列数组。
  3. end设成队列长度,这里是5。
  4. 把头和尾(head、tail)都设成0。
  5. 事件队列中有(长度+1)个空闲位置,把前置位置frontEvt算上。

使用MyQueue_post_FIFO发送事件

bool MyQueue_post_FIFO(MyQueue * const me, MyEvt const * const e)
{
	bool bState = 1;
	if (me->nFree > 0)
	{
		me->nFree--;
		if (me->frontEvt == 0)
		{
			me->frontEvt = e;
		}
		else
		{
			me->ring[me->head] = e;
			if (me->head == 0)
			{
				me->head = me->end;
			}
			me->head--;

		}
		bState = 1;
	}
	else
	{
		bState = 0;
	}
	return bState;
}
  1. 判断队列是否已满。
  2. 当前队列空闲计数器nFree减一。
  3. 前置位置frontEvt是否有事件,如果没有事件,把要发送的事件插入到前置位置frontEvt。
  4. 如果前置位置不为空,则在事件队列的头head插入事件。head减一。
  5. 由于 限制了 head 和 tail 索引的范围,它们在达到 0 时必须“绕回” end。

head索引总是递减,即使在回转后也是如次。我选择递减 tail (以及 head )索引,因为它导致一个比递增索引方法更加有效的实现。这样,回绕发生在 0 而不是在尾部。把一个数和 0 相比,远比其他比较有效率。
 

使用MyQueue_get函数获取一个事件

MyEvt const *MyQueue_get(MyQueue * const me)
{
	MyEvt const * e;
	e = me->frontEvt;
	if (e != 0)
	{
		me->nFree++;
		if (me->nFree <= me->end)/* any events in the ring buffer? */
		{
			me->frontEvt = me->ring[me->tail];
			if (me->tail == 0)
			{
				me->tail = me->end;
			}
			--me->tail;
		}
		else
		{
			me->frontEvt = 0;
		}
	}
	return e;
}
  1. 设置一个返回值的指针,取出前置位置的事件,并判断是否为空。
  2. 空闲计数器nFree加1。
  3. 判断队列内是否还存在事件,如果有,把尾部事件ring[tail]放到前置位置frontEvt。
  4. 由于 限制了 head 和 tail 索引的范围,它们在达到 0 时必须“绕回” end。
  5. 尾巴tail减一。
  6. 返回事件指针。

队列使用情况简图

QP原生事件池/内存池

动态事件允许再三地重用相同的内存来传递不同的事件。 QF 动态从某个被框架管理的事件池分配这样的事件。 QF 里的一个事件池是一个固定块尺寸的堆,也被称为内存分区或内存池。
内存池管理内存的方法是,把内存分割成所需尺寸的块并把没有使用的内存块放在一个单链表内。这组织堆栈类型的数据结构的标准技术,仅从单端存取这个结构(LIFO) 。 QMPool 也使用一个方便的技巧来链接在空闲表里的空闲块,而不需为指针消耗额外的存储。因为在空闲表的块没有被使用, QMPool 可以把这些块当作链接表指针来重用。

非空闲块作为事件存放,空闲块可以存放链表。内存回收时把该内存块转化为链表结构,作为当前空闲块free。

取内存时给出当前空闲块的指针,free改为下一个空闲块。

放内存时,给出要放的内存块地址作为free,并把指针指向之前的free。

事件池的初始化

typedef struct node
{
	struct node *pNext;
}node;

void pool_init(QMPool * me, void *poolSto, uint32_t poolSize, uint16_t blockSize)
{
	uint32_t block, n;
	node *p;
	n = 1;
	block = sizeof(node);
	while (block < blockSize)
	{
		block += sizeof(node);
		n++;
	}
	me->blockSize = block;
	p = (node*)poolSto;
	me->free = p;
	while (poolSize > blockSize)
	{
		p->pNext = &p[n];
		p = p->pNext;
		poolSize -= blockSize;
		me->nTot++;
	}
	me->nTot++;
	p->pNext = NULL;
	me->nFree = me->nTot;
	me->start = poolSto;
	me->end = p;
}
  1. 为了实现在池里所有块的对齐,我把具体的 block 尺寸取整成最接近 node尺寸乘以一个整数。所有块都是 block尺寸的乘以整数,我可以确信每个块也被对齐。
  2. 将事件池以block大小分割,并且每个块都设成node类型,并赋予指针,指向下一个空闲块。
  3. 最后一块内存池的pNext指向NULL,可以用来判断内存池是否用光。

从池里获得一个内存块

void * pool_get(QMPool * me)
{
	node * p;
	p = (node *) me->free;
	if (p != NULL)
	{
		me->free = p->pNext;
		me->nFree--;
	}
	else
	{
		printf("pool full!");
	}
	return p;
}
  1. 给出当前空闲块free,判断free是否为空,若为空,表示内存池已满。
  2. 把当前空闲指针指向下一个空闲块。
  3. 空闲内存块计数器减一。

把一个内存块回收到池内
 

void pool_put(QMPool * me, void * p)
{
	if (p <= me->end && p >= me->start)
	{
		((node*)p)->pNext = me->free;
		me->free = p;
		me->nFree++;
	}
	else
	{
		printf("pool put err!");
	}

}
  1. 判断要放入内存池的内存是否在内存池范围内
  2. 该内存块的pNext指向当前空闲内存块
  3. 当前空闲内存块设置为要回收的内存块
  4. 空闲内存块计数器加一。

VS测试代码

static MyEvt const  * MyQueueSto[5]; //某个活动对象的事件队列
typedef struct QActive {
    QHsm super; 

    QF_EQUEUE_TYPE eQueue; // 活动对象的事件

    QF_OS_OBJECT_TYPE osObject;

    QF_THREAD_TYPE thread;

    uint8_t prio;

    uint8_t startPrio;

} QActive;

typedef struct QEQueue {
  
    QEvt const * volatile frontEvt;

    QEvt const **ring;

    QEQueueCtr end;

    QEQueueCtr volatile head;

    QEQueueCtr volatile tail;

    QEQueueCtr volatile nFree;

    QEQueueCtr nMin;
} QEQueue;

队列


int Queue_int(MyQueue * const me, MyEvt const *qSto[], uint_fast16_t const qLen)
{
	me->frontEvt = (MyEvt const *)0; /* no events in the queue */
	me->ring = &qSto[0];        /* the beginning of the ring buffer */
	me->end = (uint_fast16_t)qLen;
	if (qLen != (uint_fast16_t)0) {
		me->head = (uint_fast16_t)0;
		me->tail = (uint_fast16_t)0;
	}
	me->nFree = (uint_fast16_t)(qLen + (uint_fast16_t)1); /*+1 for frontEvt */
	me->nMin = me->nFree;
	return 0;
}

bool MyQueue_post_FIFO(MyQueue * const me, MyEvt const * const e)
{
	bool bState = 1;
	if (me->nFree > 0)
	{
		me->nFree--;
		if (me->frontEvt == 0)
		{
			me->frontEvt = e;
		}
		else
		{
			me->ring[me->head] = e;
			if (me->head == 0)
			{
				me->head = me->end;
			}
			me->head--;

		}
		bState = 1;
	}
	else
	{
		bState = 0;
	}
	return bState;
}

MyEvt const *MyQueue_get(MyQueue * const me)
{
	MyEvt const * e;
	e = me->frontEvt;
	if (e != 0)
	{
		me->nFree++;
		if (me->nFree <= me->end)/* any events in the ring buffer? */
		{
			me->frontEvt = me->ring[me->tail];
			if (me->tail == 0)
			{
				me->tail = me->end;
			}
			--me->tail;
		}
		else
		{
			me->frontEvt = 0;
		}
	}
	return e;
}

事件池


void pool_init(QMPool * me, void *poolSto, uint32_t poolSize, uint16_t blockSize)
{
	uint32_t block, n;
	node *p;
	n = 1;
	block = sizeof(node);
	while (block < blockSize)
	{
		block += sizeof(node);
		n++;
	}
	me->blockSize = block;
	p = (node*)poolSto;
	me->free = p;
	while (poolSize > blockSize)
	{
		p->pNext = &p[n];
		p = p->pNext;
		poolSize -= blockSize;
		me->nTot++;
	}
	me->nTot++;
	p->pNext = NULL;
	me->nFree = me->nTot;
	me->start = poolSto;
	me->end = p;
}

void * pool_get(QMPool * me)
{
	node * p;
	p = (node *) me->free;
	if (p != NULL)
	{
		me->free = p->pNext;
		me->nFree--;
	}
	else
	{
		printf("pool full!");
	}
	return p;
}

void pool_put(QMPool * me, void * p)
{
	if (p <= me->end && p >= me->start)
	{
		((node*)p)->pNext = me->free;
		me->free = p;
		me->nFree++;
	}
	else
	{
		printf("pool put err!");
	}

}

头文件


#define QF_EQUEUE_TYPE  QEQueue

typedef struct node
{
	struct node *pNext;
}node;

typedef struct QMPoolTag QMPool;

struct QMPoolTag {

	void *start;

	void *end;

	void *free;

	uint16_t blockSize;

	uint16_t nTot;

	uint16_t nFree;

	uint16_t nMin;
};


typedef struct {
	uint32_t sig;              /*!< signal of the event instance */
	uint32_t uiDataH;
	uint32_t uiDataL;
} MyEvt;

typedef struct MyQueue {

	MyEvt const * volatile frontEvt;

	MyEvt const **ring;

	uint16_t end;

	uint16_t volatile head;

	uint16_t volatile tail;

	uint16_t volatile nFree;

	uint16_t nMin;
}MyQueue;

void pool_init(QMPool * me, void *poolSto, uint32_t poolSize, uint16_t blockSize);

void * pool_get(QMPool * me);

void pool_put(QMPool * me, void * p);

主函数


void main(void)
{
	int iKey1;
	int iKey2;
	MyEvt *eNewEvt;
	MyEvt *eEvtCur;
	bool bIsEmpty;
	pool_init(&EvtPool,MyEvtPoolSto,sizeof(MyEvtPoolSto),sizeof(MyEvtPoolSto[0]));
	Queue_int(&QueueTest, &MyQueueSto, Q_DIM(MyQueueSto));

	while (1)
	{
		scanf_s("%d", &iKey1);

		if (iKey1 == 11)
		{
			printf("请输入事件信号:");
			scanf_s("%d", &iKey2);
			eNewEvt = (MyEvt*)pool_get(&EvtPool);
			if (eNewEvt != NULL)
			{
				eNewEvt->sig = iKey2;
				bIsEmpty = MyQueue_post_FIFO(&QueueTest,eNewEvt);
			}
			else
			{
				printf("事件池已满\n");
			}
			
			if (bIsEmpty == 0)
			{
				printf("队列已满\n");
			}
		}
		else if (iKey1 == 12)
		{
			eEvtCur = (MyEvt * )MyQueue_get(&QueueTest);
			
			if (eEvtCur != 0)
			{
				printf("取出的事件信号为%d\n", eEvtCur->sig);
			}
			else
			{
				printf("队列内事件为空");
			}
			pool_put(&EvtPool, eEvtCur);
		}
	}
}

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值