首页 > 其他分享 >任务延时队列

任务延时队列

时间:2024-12-14 19:28:22浏览次数:11  
标签:task tTask 队列 void 任务 延时

目录

现有问题

延时队列设计

设计实现


现有问题

  • 每次时钟节拍都需要扫描所有任务,比较耗时
  • 不易支持多个任务具有相同优先级

延时队列设计

将所有需要延时的任务单独放置在一个队列中,每次发生系统节拍时,只需扫描该队列。

方式一:独立保存延时时间

  • 插入延时任务比较简单,快速。
  • 扫描整个队列比较简单,但较慢。

方式二:递增的延时队列

每一个任务的延时时间等于它前面所有任务的延时时间的和加上它自己的延时时间。

  • 插入延时任务比较复杂,较慢。
  • 扫描整个队列比较复杂,但较快。

设计实现

  • 添加延时队列
  • 延时队列的插入
  • 延时队列的删除
  • 时钟节拍扫描延时队列

main.c

#include "tinyOS.h"
#include "ARMCM3.h"

tTask *currentTask;			//指示当前运行任务的指针
tTask *nextTask;				//指向下一个任务的指针
tTask *idleTask;				//空闲任务


tBitmap taskPrioBitmap;	//优先级位图
tTask *taskTable[TINYOS_PRO_COUNT];	//任务数组列表

uint8_t schedLockCount;	//调度锁计数器

tList tTaskDelayedList;		//延时队列

/* 任务的初始化函数 */
//参数:tTask结构的指针,任务入口函数的地址,传递给任务的参数地址,堆栈地址
//任务初始运行时,会把栈里的内容依次弹出来,恢复到内核寄存器中。
void tTaskInit(tTask *task, void(*entry)(void *), void *param, uint32_t prio, tTaskStack *stack)
{
	//初始化具体的堆栈内容
	//传递堆栈的末端地址,内核本身的堆栈增长方式是满递减方式增长的,先进行递减操作
	*(--stack) = (unsigned long)(1 << 24);	//xPSR,设置T标志位
	*(--stack) = (unsigned long)entry;			//R15(PC),程序入口函数
	*(--stack) = (unsigned long)0x14;				//R14(LR),未用
	*(--stack) = (unsigned long)0x12;				//R12,未用
	*(--stack) = (unsigned long)0x3;				//R3,未用
	*(--stack) = (unsigned long)0x2;				//R2,未用
	*(--stack) = (unsigned long)0x1;				//R1,未用
	*(--stack) = (unsigned long)param;			//R0,程序的入口参数,函数第一个参数存入R0
	*(--stack) = (unsigned long)0x11;				//R11,未用
	*(--stack) = (unsigned long)0x10;				//R10,未用
	*(--stack) = (unsigned long)0x9;				//R9,未用
	*(--stack) = (unsigned long)0x8;				//R8,未用
	*(--stack) = (unsigned long)0x7;				//R7,未用
	*(--stack) = (unsigned long)0x6;				//R6,未用
	*(--stack) = (unsigned long)0x5;				//R5,未用
	*(--stack) = (unsigned long)0x4;				//R4,未用
	
	
	task->stack = stack;	//保存最终的值
	task->delayTicks = 0;	//初始化计数器
	task->prio = prio;		//初始化优先级
	task->state = TINYOS_TASK_STATE_RDY;	//任务状态初始化为就绪态
	
	tNodeInit(&(task->delayNode));				//对延时结点初始化
	
	taskTable[prio] = task;
	tBitmapSet(&taskPrioBitmap, prio);		//对位图置1,表明任务已经初始化好,可以占用CPU运行
}

/* 查找优先级位图函数 */
tTask *tTaskHighestReady(void)
{
	uint32_t highestPrio = tBitmaoFirstSet(&taskPrioBitmap);
	return taskTable[highestPrio];
}

/* 调度锁初始化函数 */
void tTaskSchedInit(void)
{
	schedLockCount = 0;						//初始计数值为0
	tBitmapInit(&taskPrioBitmap);	//对bitmap初始化
}

/* 上锁函数(禁止调度函数) */
void tTaskSchedDisable(void)
{
	uint32_t status = tTaskEnterCritical();
	//schedLockCount是全局变量需要临界区保护
	if(schedLockCount < 255)//schedLockCount是8位的,防止溢出
	{
		schedLockCount++;
	}
	tTaskExitCritical(status);
}

/* 解锁函数(使能调度函数) */
void tTaskSchedEnable(void)
{
	uint32_t status = tTaskEnterCritical();
	if(schedLockCount > 0)
	{
		if(--schedLockCount == 0)
		{
			tTaskSched();//执行调度函数
		}
	}
	tTaskExitCritical(status);
}

/* 就绪表插入任务 */
void tTaskSchedRdy(tTask *task)
{
	taskTable[task->prio] = task;
	tBitmapSet(&taskPrioBitmap, task->prio);	//就绪表中设置任务就绪
}

/* 就绪表移除任务 */
void tTaskSchedUnRdy(tTask *task)
{
	taskTable[task->prio] = (tTask *)0;
	tBitmapClear(&taskPrioBitmap, task->prio);	//位图位清零
}

/* 调度函数 */
//决定CPU在哪两个任务之间运行,采用什么规则,怎么分配
void tTaskSched()
{
	tTask *tempTask;
	uint32_t status = tTaskEnterCritical();
	
	//判断调度器是否上锁
	if(schedLockCount > 0)//上锁
	{
		tTaskExitCritical(status);//退出
		return;
	}
	
	tempTask = tTaskHighestReady();//获取最高优先级需要占用CPU运行的任务
	if(tempTask != currentTask)//最高优先级任务与当前任务不同
	{
		nextTask = tempTask;
		tTaskSwitch();//任务切换函数
	}
	
	tTaskExitCritical(status);
}

/* 延时队列初始化 */
void tTaskDelayedInit()
{
	tListInit(&tTaskDelayedList);//初始化延时链表
}

/* 延时队列插入任务 */
void tTimeTaskWait(tTask *task, uint32_t ticks)
{
	task->delayTicks = ticks;
	tListAddLast(&tTaskDelayedList, &(task->delayNode));//任务插入队列尾部
	task->state |= TINYOS_TASK_STATE_DELAYED;
}

/* 延时队列移除任务 */
void tTimeTaskWakeUp(tTask *task)
{
	tListRemove(&tTaskDelayedList, &(task->delayNode));
	task->state &= ~TINYOS_TASK_STATE_DELAYED;//清除延时标志位
}

/* 时钟节拍处理函数 */
void tTaskSystemTickHandler()
{
	tNode *node;
	uint32_t status = tTaskEnterCritical();
	//扫描延时队列
	for(node = tTaskDelayedList.headNode.nextNode; node != &(tTaskDelayedList.headNode); node = node->nextNode)
	{
		tTask *task = tNodeParent(node, tTask, delayNode);//获取任务结构
		if(--task->delayTicks == 0)//判断任务有没有延时到
		{
			tTimeTaskWakeUp(task);//从延时队列中移除
			
			tTaskSchedRdy(task);//插入就绪表
		}
	}
	tTaskExitCritical(status);
	tTaskSched();//调度函数
}

/* 任务延时函数 */
void tTaskDelay(uint32_t delay)
{
	uint32_t status = tTaskEnterCritical();
	
	tTimeTaskWait(currentTask, delay);
	
	tTaskSchedUnRdy(currentTask);//清除就绪位,将调用延时函数的任务从就绪表中删除掉

	tTaskExitCritical(status);
	tTaskSched();//调度函数
}

/* SysTick初始化函数 */
//参数:毫秒
void tSetSysTickPeriod(uint32_t ms)
{
	SysTick->LOAD = ms * SystemCoreClock / 1000 - 1;//初始化SysTick重载寄存器值,在SysTick计数器递减到0时将这个计数器的值加载到递减寄存器里
	NVIC_SetPriority(SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);//
	SysTick->VAL = 0;//递减寄存器
	SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |	//控制寄存器
									SysTick_CTRL_TICKINT_Msk	|		//产生中断使能的标志位
									SysTick_CTRL_ENABLE_Msk;			//使能计数器的标志位
}

/* SysTick中断处理函数 */
void SysTick_Handler()
{
	tTaskSystemTickHandler();
}

//定义两个任务要执行的功能
int task1Flag;
void task1Entry(void *param)
{
	tSetSysTickPeriod(10);//初始化
	
	for(;;)//任务里是for的死循环
	{
		task1Flag = 0;
		tTaskDelay(1);
		task1Flag = 1;
		tTaskDelay(1);
	}
}
int task2Flag;
void task2Entry(void *param)
{
	for(;;)
	{
		task2Flag = 0;
		tTaskDelay(1);
		task2Flag = 1;
		tTaskDelay(1);
	}
}

//定义两个任务,分别为它们配备独立的堆栈空间
tTask tTask1;
tTask tTask2;
tTaskStack task1Env[1024];
tTaskStack task2Env[1024];

tTask tTaskIdle;							//空闲任务
tTaskStack idleTaskEnv[1024];	//空闲任务的堆栈

/* 空闲任务具体内容 */
void idleTaskEntry(void *param)
{
	for(;;)
	{
		//空闲任务什么都不做
	}
}

int main(void)
{
	tTaskSchedInit();		//初始化调度锁
	tTaskDelayedInit();	//初始化延时队列
	
	//最后一个参数:传堆栈末端地址,因为堆栈是向下生长的,初始堆栈地址是堆栈空间最后一个单元地址的末端
	tTaskInit(&tTask1, task1Entry, (void *)0x11111111, 0, &task1Env[1024]);
	tTaskInit(&tTask2, task2Entry, (void *)0x22222222, 1, &task2Env[1024]);
	
	//初始化任务数组
	taskTable[0] = &tTask1;
	taskTable[1] = &tTask2;
	
	tTaskInit(&tTaskIdle, idleTaskEntry, (void *)0, TINYOS_PRO_COUNT - 1, &idleTaskEnv[1024]);//创建空闲任务
	idleTask = &tTaskIdle;//空闲任务赋值
	
	nextTask = tTaskHighestReady();//初始运行任务指向就绪表中优先级最高的任务
	
	tTaskRunFirst();//切换到第0个任务运行
	
	return 0;
	//执行tTaskRunFirst函数后,CPU的控制权切换到任务里运行
	//任务里是for循环会一直在任务里运行
	//切换到另一个函数里面也是for循环
	//永远不会返回
}

tinyOS.h

#ifndef __TINYOS_H
#define __TINYOS_H

#include <stdint.h>
#include "tLib.h"
#include "tConfig.h"

#define TINYOS_TASK_STATE_RDY				0					//任务就绪
#define TINYOS_TASK_STATE_DELAYED		(1 << 1) 	//任务处于延时状态

//堆栈单元类型
typedef uint32_t tTaskStack;

//任务结构
typedef struct _tTask{
	uint32_t *stack;			//指向堆栈的指针
	uint32_t delayTicks;	//软延时计数器
	tNode delayNode;			//通用的结点结构
	uint32_t prio;				//优先级字段
	uint32_t state;				//任务状态
}tTask;

extern tTask *currentTask;			
extern tTask *nextTask;				

uint32_t tTaskEnterCritical(void);
void tTaskExitCritical(uint32_t status);

void tTaskSwitch(void);		//和CPU相关,写在switch.c
void tTaskRunFirst(void);

void tTaskSchedInit(void);
void tTaskSchedDisable(void);
void tTaskSchedEnable(void);
void tTaskSched();

#endif

调试现象

标签:task,tTask,队列,void,任务,延时
From: https://blog.csdn.net/daybydayby/article/details/144365449

相关文章

  • 定时/延时任务-Kafka时间轮源码分析
    文章目录1.概要2.TimingWheel2.1核心参数2.2添加任务2.3推进时间3.TimerTaskList3.1添加节点3.2删除节点3.3刷新链表3.4队列相关4.时间轮链表节点-TimerTaskEntry5.TimerTask6.Timer和SystemTimer-设计降级逻辑7.上层调用8.小结1.概要时间轮的......
  • 【深度学习|语义分割之UNet】继承自 PyTorch 的 nn.Module的UNet——基于编码器-解码
    【深度学习|语义分割之UNet】继承自PyTorch的nn.Module的UNet——基于编码器-解码器结构的语义分割任务的卷积神经网络。附代码及解读。【深度学习|语义分割之UNet】继承自PyTorch的nn.Module的UNet——基于编码器-解码器结构的语义分割任务的卷积神经网络。附代码及......
  • 【Linux】:多线程(POSIX 信号量 、基于环形队列的生产消费者模型)
    ......
  • 开拓计划1 - 栈与队列
    开拓计划1-栈与队列栈与队列的概念及作用栈的概念Q:什么是栈?A:栈是一种后进先出(BIFO)的数据结构。栈的作用Q:栈有什么作用?A:只要满足栈的定义的场景都可以使用栈。eg:括号匹配,火车进站etc.计算后缀表达式时也会使用。队列的概念Q:什么是队列?A:队列是一种先进先......
  • GPU加速主要通过并行化计算任务,适合大规模数据处理和计算密集型应用。 多线程并发适用
    GPU加速和多线程并发是提高计算密集型任务性能的两种重要技术。它们在不同领域(如深度学习、科学计算、图像处理等)得到了广泛应用。下面,我将详细介绍这两者的概念、原理、以及如何结合它们进行高效计算。1. GPU加速1.1 GPU加速的基本原理GPU(图形处理单元)最初设计用于图像和视......
  • 效率管理软件全解析:从任务管理到团队协作的最佳选择
    在现代社会,工作节奏日益加快,信息流动迅速,如何高效地管理时间、任务和团队成为了每个职场人必须面对的重要课题。效率管理工具作为提高工作效率的关键工具,越来越成为个人和团队必不可少的助手。从个人任务管理到团队协作,效率管理工具通过优化工作流程、简化操作步骤和提升沟通效率,......
  • 跨境电商独立站怎么做 | 教你建立全球都能低延时访问的跨境电商网站
    今年开始,国家明确开始加大鼓励跨境电商行业。你选择在海外电商网站开设店铺,也可以选择建立跨境电商独立站,即自主搭建的、拥有独立域名的跨境电商网站。这种网站不依赖于任何第三方电商平台,不需要向平台交佣金,可以自主运营和管理,直接面向全球消费者进行商品或服务的销售。做......
  • ‌Bull是一个基于Redis的队列库,专为Node.js设计
    ‌Bull是一个基于Redis的队列库,专为Node.js设计,提供高性能和可靠的任务处理能力‌。它通过Redis作为后端存储,确保任务的持久性和高可用性。Bull的设计理念是简单、高效,同时具备强大的功能,能够满足各种复杂的任务调度需求‌12。Bull的主要功能‌高性能‌:Bull采用无轮询设计,极大......
  • 【捡垃圾】使用4.2寸三色墨水屏做一个任务待办清单
    此篇文章在2024年1月9日被记录熟悉了解本章博客需要你有较为入门的编程基础,包括但是不限于C语言、python1、前言最近在老五这里看到4.2寸的三色墨水屏只要7.5一张,于是下单了两个,到货后发现是全新设备,保护膜都在,拆开后发现是telink8359处理器,3个纽扣电池,这个价格还要什么自......
  • ProcessExplorer 多功能任务管理器软件-中文绿色单文件版
    今天我和大家分享一款系统监控工具——ProcessExplorer。一个比Windows自带的任务管理器更强大的工具。感觉最实用的是他的搜索功能,可以搜到系统任务管理器里面无法显示的应用,大家可以网上走索下载,也可以通过这个链接下载:https://www.itlooker.cn/archives/6204ProcessExplore......