堆栈使用测量

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); 
}

参考资料:

  1. 【李述铜】从0到1自己动手写嵌入式操作系统
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值