1 堆栈使用测量
1.1 问题概述
我们知道在当前的设计中每个任务都配备一个有限的任务栈,用于保存各种关键性的数据。但是这个栈空间是有限的,实际有可能超出此空间。

可能出现如下错误:

1.2 设计原理
设计原理如下:

注意事项:

1.3 设计实现
清空堆栈空间:

统计堆栈使用情况:

需要修改task结构体:
// 任务结构:包含了一个任务的所有信息
typedef struct _tTask {
// 任务所用堆栈的当前堆栈指针。每个任务都有他自己的堆栈,用于在运行过程中存储临时变量等一些环境参数
// 在tinyOS运行该任务前,会从stack指向的位置处,会读取堆栈中的环境参数恢复到CPU寄存器中,然后开始运行
// 在切换至其它任务时,会将当前CPU寄存器值保存到堆栈中,等待下一次运行该任务时再恢复。
// stack保存了最后保存环境参数的地址位置,用于后续恢复
uint32_t * stack;
// 堆栈的起即地址
uint32_t * stackBase;
// 堆栈的总容量
uint32_t stackSize;
// 连接结点
tNode linkNode;
// 任务延时计数器
uint32_t delayTicks;
// 延时结点:通过delayNode就可以将tTask放置到延时队列中
tNode delayNode;
// 任务的优先级
uint32_t prio;
// 任务当前状态
uint32_t state;
// 当前剩余的时间片
uint32_t slice;
// 被挂起的次数
uint32_t suspendCount;
// 任务被删除时调用的清理函数
void (*clean) (void * param);
// 传递给清理函数的参数
void * cleanParam;
// 请求删除标志,非0表示请求删除
uint8_t requestDeleteFlag;
// 任务正在等待的事件类型
struct _tEvent * waitEvent;
// 等待事件的消息存储位置
void * eventMsg;
// 等待事件的结果
uint32_t waitEventResult;
// 等待的事件方式
uint32_t waitFlagsType;
// 等待的事件标志
uint32_t eventFlags;
}tTask;
// 任务相关信息结构
typedef struct _tTaskInfo {
// 任务延时计数器
uint32_t delayTicks;
// 任务的优先级
uint32_t prio;
// 任务当前状态
uint32_t state;
// 当前剩余的时间片
uint32_t slice;
// 被挂起的次数
uint32_t suspendCount;
// 堆栈的总容量
uint32_t stackSize;
// 堆栈空余量
uint32_t stackFree;
}tTaskInfo;
任务初始化函数和任务查询接口也需要修改如下:
/**********************************************************************************************************
** Function name : tTaskInit
** Descriptions : 初始化任务结构
** parameters : task 要初始化的任务结构
** parameters : entry 任务的入口函数
** parameters : param 传递给任务的运行参数
** Returned value : 无
***********************************************************************************************************/
void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t prio, uint32_t * stack, uint32_t size)
{
uint32_t * stackTop;
// 为了简化代码,tinyOS无论是在启动时切换至第一个任务,还是在运行过程中在不同间任务切换
// 所执行的操作都是先保存当前任务的运行环境参数(CPU寄存器值)的堆栈中(如果已经运行运行起来的话),然后再
// 取出从下一个任务的堆栈中取出之前的运行环境参数,然后恢复到CPU寄存器
// 对于切换至之前从没有运行过的任务,我们为它配置一个“虚假的”保存现场,然后使用该现场恢复。
task->stackBase = stack;
task->stackSize = size;
memset(stack, 0, size);
// 注意以下两点:
// 1、不需要用到的寄存器,直接填了寄存器号,方便在IDE调试时查看效果;
// 2、顺序不能变,要结合PendSV_Handler以及CPU对异常的处理流程来理解
stackTop = stack + size / sizeof(tTaskStack);
*(--stackTop) = (unsigned long)(1<<24); // XPSR, 设置了Thumb模式,恢复到Thumb状态而非ARM状态运行
*(--stackTop) = (unsigned long)entry; // 程序的入口地址
*(--stackTop) = (unsigned long)0x14; // R14(LR), 任务不会通过return xxx结束自己,所以未用
*(--stackTop) = (unsigned long)0x12; // R12, 未用
*(--stackTop) = (unsigned long)0x3; // R3, 未用
*(--stackTop) = (unsigned long)0x2; // R2, 未用
*(--stackTop) = (unsigned long)0x1; // R1, 未用
*(--stackTop) = (unsigned long)param; // R0 = param, 传给任务的入口函数
*(--stackTop) = (unsigned long)0x11; // R11, 未用
*(--stackTop) = (unsigned long)0x10; // R10, 未用
*(--stackTop) = (unsigned long)0x9; // R9, 未用
*(--stackTop) = (unsigned long)0x8; // R8, 未用
*(--stackTop) = (unsigned long)0x7; // R7, 未用
*(--stackTop) = (unsigned long)0x6; // R6, 未用
*(--stackTop) = (unsigned long)0x5; // R5, 未用
*(--stackTop) = (unsigned long)0x4; // R4, 未用
task->slice = TINYOS_SLICE_MAX; // 初始化任务的时间片计数
task->stack = stackTop; // 保存最终的值
task->prio = prio; // 设置任务的优先级
task->state = TINYOS_TASK_STATE_RDY; // 设置任务为就绪状态
task->suspendCount = 0; // 初始挂起次数为0
task->clean = (void(*)(void *))0; // 设置清理函数
task->cleanParam = (void *)0; // 设置传递给清理函数的参数
task->requestDeleteFlag = 0; // 请求删除标记
task->waitEvent = (tEvent *)0; // 没有等待事件
task->eventMsg = (void *)0; // 没有等待事件
task->waitEventResult = tErrorNoError; // 没有等待事件错误
tNodeInit(&(task->delayNode)); // 初始化延时结点
tNodeInit(&(task->linkNode)); // 初始化链接结点
tTaskSchedRdy(task); // 将任务插入就绪队列
}
/**********************************************************************************************************
** Function name : tTaskGetInfo
** Descriptions : 获取任务相关信息
** parameters : task 需要查询的任务
** parameters : info 任务信息存储结构
** Returned value : 无
***********************************************************************************************************/
void tTaskGetInfo (tTask * task, tTaskInfo * info)
{
uint32_t * stackEnd;
// 进入临界区
uint32_t status = tTaskEnterCritical();
info->delayTicks = task->delayTicks; // 延时信息
info->prio = task->prio; // 任务优先级
info->state = task->state; // 任务状态
info->slice = task->slice; // 剩余时间片
info->suspendCount = task->suspendCount; // 被挂起的次数
info->stackSize = task->stackSize;
// 计算堆栈使用量
info->stackFree = 0;
stackEnd = task->stackBase;
while ((*stackEnd++ == 0) && (stackEnd <= task->stackBase + task->stackSize / sizeof(tTaskStack)))
{
info->stackFree++;
}
// 转换成字节数
info->stackFree *= sizeof(tTaskStack);
// 退出临界区
tTaskExitCritical(status);
}
参考资料:
2753

被折叠的 条评论
为什么被折叠?



