操作系统栈溢出检測之ucosII篇

操作系统栈溢出检測之ucosII篇

大家好,又见面了,我是全栈君。

操作系统栈溢出检測之uc/osII篇

Author               :       David Lin (林鹏)

E-mail               :       linpeng1577@gmail.com  linpeng1577@163.com  15820224344@163.com

OS                   :       源代码级理解掌握Ucos,Rt-thread等嵌入式操作系统内核的设计与实现,眼下在研究linux内核,路漫漫其修远兮,吾将上下而求索 :)

转载请注明出处,谢谢

前言:

         在嵌入式操作系统执行中。进程的栈溢出问题是大家比較关心的问题。由于资源限制。栈大小受到限制,本文主要介绍uc/os自带的栈检測机制(为什么是ucos,而不是ecos或者其它,由于我如今项目用这个,兴许有时间再介绍一种更简单通用。不依赖详细操作系统的栈溢出检測机制)。

原理:

         1.在进程的栈初始化的时候。按预设的字节对齐方式,用特定魔数(比方0x9527,ucosII预设是0值)将栈元素依次完毕初始化;

         2.在系统执行的时候,通过守护进程。依次遍历各个进程的栈。检測栈元素的值是否等于初始化的魔数,求得被污染的元素个数所占栈大小的比例,即为栈使用量。

         3.不通过守护进程,直接对进程进行栈溢出检測是否可行?能够。只是不推荐在进程内部或者进程调度的时候进行如此使用量检測。即使使用二分查找算法进行优化,假设栈大到一定程度,效 率问题还是须要谨慎考虑的;

         4.不扯为什么这么设计这些事情了,这跟小学语文写作者的中心思想一样,不是ucos原作者,所以不写,你想知道Jean J. Labrosse怎么想的,能够给他发邮件:-)

系统版本号:uc/osII V2.85

开发环境:IAR for ARM (为什么不在linux下开发,我也想知道。

                    由于我喜欢gnu/linux,喜欢用gcc,gdb,vim,make,(⊙o⊙)…。只是,你懂的:-)                     

1.      相关宏定义os_cfg.h

#defineOS_TASK_STAT_EN                        1  /* Enable (1) or Disable(0) the statistics task*/

#defineOS_TASK_STAT_STK_CHK_EN   1  /* Check task stacks from statistic task          */

 

2.      相关函数os_core.c os_task.c

static  void OS_InitTaskStat (void) {……};     //初始化栈守护进程,看哪个进程最贪吃 :-)

/*
*********************************************************************************************************
*                                             INITIALIZATION
*                                      CREATING THE STATISTIC TASK
*
* Description: This function creates the Statistic Task.
*
* Arguments  : none
*
* Returns    : none
*********************************************************************************************************
*/

#if OS_TASK_STAT_EN > 0
static  void  OS_InitTaskStat (void)
{
#if OS_TASK_NAME_SIZE > 7
    INT8U  err;
#endif


#if OS_TASK_CREATE_EXT_EN > 0
    #if OS_STK_GROWTH == 1
    (void)OSTaskCreateExt(OS_TaskStat,
                          (void *)0,                                   /* No args passed to OS_TaskStat()*/
                          &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1],   /* Set Top-Of-Stack               */
                          OS_TASK_STAT_PRIO,                           /* One higher than the idle task  */
                          OS_TASK_STAT_ID,
                          &OSTaskStatStk[0],                           /* Set Bottom-Of-Stack            */
                          OS_TASK_STAT_STK_SIZE,
                          (void *)0,                                   /* No TCB extension               */
                          OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);  /* Enable stack checking + clear  */
    #else
    (void)OSTaskCreateExt(OS_TaskStat,
                          (void *)0,                                   /* No args passed to OS_TaskStat()*/
                          &OSTaskStatStk[0],                           /* Set Top-Of-Stack               */
                          OS_TASK_STAT_PRIO,                           /* One higher than the idle task  */
                          OS_TASK_STAT_ID,
                          &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1],   /* Set Bottom-Of-Stack            */
                          OS_TASK_STAT_STK_SIZE,
                          (void *)0,                                   /* No TCB extension               */
                          OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);  /* Enable stack checking + clear  */
    #endif
#else
    #if OS_STK_GROWTH == 1
    (void)OSTaskCreate(OS_TaskStat,
                       (void *)0,                                      /* No args passed to OS_TaskStat()*/
                       &OSTaskStatStk[OS_TASK_STAT_STK_SIZE - 1],      /* Set Top-Of-Stack               */
                       OS_TASK_STAT_PRIO);                             /* One higher than the idle task  */
    #else
    (void)OSTaskCreate(OS_TaskStat,
                       (void *)0,                                      /* No args passed to OS_TaskStat()*/
                       &OSTaskStatStk[0],                              /* Set Top-Of-Stack               */
                       OS_TASK_STAT_PRIO);                             /* One higher than the idle task  */
    #endif
#endif

#if OS_TASK_NAME_SIZE > 14
    OSTaskNameSet(OS_TASK_STAT_PRIO, (INT8U *)"uC/OS-II Stat", &err);
#else
#if OS_TASK_NAME_SIZE > 7
    OSTaskNameSet(OS_TASK_STAT_PRIO, (INT8U *)"OS-Stat", &err);
#endif
#endif
}
#endif

#if OS_TASK_CREATE_EXT_EN > 0

void  OS_TaskStkClr (OS_STK *pbos, INT32U size,INT16U opt) {……};  //用于栈初始化

#endif

/*
*********************************************************************************************************
*                                        CLEAR TASK STACK
*
* Description: This function is used to clear the stack of a task (i.e. write all zeros)
*
* Arguments  : pbos     is a pointer to the task's bottom of stack.  If the configuration constant
*                       OS_STK_GROWTH is set to 1, the stack is assumed to grow downward (i.e. from high
*                       memory to low memory).  'pbos' will thus point to the lowest (valid) memory
*                       location of the stack.  If OS_STK_GROWTH is set to 0, 'pbos' will point to the
*                       highest memory location of the stack and the stack will grow with increasing
*                       memory locations.  'pbos' MUST point to a valid 'free' data item.
*
*              size     is the number of 'stack elements' to clear.
*
*              opt      contains additional information (or options) about the behavior of the task.  The
*                       LOWER 8-bits are reserved by uC/OS-II while the upper 8 bits can be application
*                       specific.  See OS_TASK_OPT_??

? in uCOS-II.H.** Returns : none**********************************************************************************************************/#if OS_TASK_CREATE_EXT_EN > 0void OS_TaskStkClr (OS_STK *pbos, INT32U size, INT16U opt){ if ((opt & OS_TASK_OPT_STK_CHK) != 0x0000) { /* See if stack checking has been enabled */ if ((opt & OS_TASK_OPT_STK_CLR) != 0x0000) { /* See if stack needs to be cleared */#if OS_STK_GROWTH == 1 while (size > 0) { /* Stack grows from HIGH to LOW memory */ size--; *pbos++ = (OS_STK)0; /* Clear from bottom of stack and up! */ }#else while (size > 0) { /* Stack grows from LOW to HIGH memory */ size--; *pbos-- = (OS_STK)0; /* Clear from bottom of stack and down */ }#endif } }}#endif

#if OS_TASK_CREATE_EXT_EN > 0

INT8U OSTaskStkChk (INT8U prio, OS_STK_DATA *p_stk_data) {……}; 

//用于检測各个进程的栈使用率,被守护进程OS_TaskStat(void *p_arg)调用

#endif

/*
*********************************************************************************************************
*                                             STACK CHECKING
*
* Description: This function is called to check the amount of free memory left on the specified task's
*              stack.
*
* Arguments  : prio          is the task priority
*
*              p_stk_data    is a pointer to a data structure of type OS_STK_DATA.
*
* Returns    : OS_ERR_NONE            upon success
*              OS_ERR_PRIO_INVALID    if the priority you specify is higher that the maximum allowed
*                                     (i.e. > OS_LOWEST_PRIO) or, you have not specified OS_PRIO_SELF.
*              OS_ERR_TASK_NOT_EXIST  if the desired task has not been created or is assigned to a Mutex PIP
*              OS_ERR_TASK_OPT        if you did NOT specified OS_TASK_OPT_STK_CHK when the task was created
*              OS_ERR_PDATA_NULL      if 'p_stk_data' is a NULL pointer
*********************************************************************************************************
*/
#if OS_TASK_CREATE_EXT_EN > 0
INT8U  OSTaskStkChk (INT8U prio, OS_STK_DATA *p_stk_data)
{
    OS_TCB    *ptcb;
    OS_STK    *pchk;
    INT32U     free;
    INT32U     size;
#if OS_CRITICAL_METHOD == 3                            /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr = 0;
#endif



#if OS_ARG_CHK_EN > 0
    if (prio > OS_LOWEST_PRIO) {                       /* Make sure task priority is valid             */
        if (prio != OS_PRIO_SELF) {
            return (OS_ERR_PRIO_INVALID);
        }
    }
    if (p_stk_data == (OS_STK_DATA *)0) {              /* Validate 'p_stk_data'                        */
        return (OS_ERR_PDATA_NULL);
    }
#endif
    p_stk_data->OSFree = 0;                            /* Assume failure, set to 0 size                */
    p_stk_data->OSUsed = 0;
    OS_ENTER_CRITICAL();
    if (prio == OS_PRIO_SELF) {                        /* See if check for SELF                        */
        prio = OSTCBCur->OSTCBPrio;
    }
    ptcb = OSTCBPrioTbl[prio];
    if (ptcb == (OS_TCB *)0) {                         /* Make sure task exist                         */
        OS_EXIT_CRITICAL();
        return (OS_ERR_TASK_NOT_EXIST);
    }
    if (ptcb == OS_TCB_RESERVED) {
        OS_EXIT_CRITICAL();
        return (OS_ERR_TASK_NOT_EXIST);
    }
    if ((ptcb->OSTCBOpt & OS_TASK_OPT_STK_CHK) == 0) { /* Make sure stack checking option is set       */
        OS_EXIT_CRITICAL();
        return (OS_ERR_TASK_OPT);
    }
    free = 0;
    size = ptcb->OSTCBStkSize;
    pchk = ptcb->OSTCBStkBottom;
    OS_EXIT_CRITICAL();
#if OS_STK_GROWTH == 1
    while (*pchk++ == (OS_STK)0) {                    /* Compute the number of zero entries on the stk */
        free++;
    }
#else
    while (*pchk-- == (OS_STK)0) {
        free++;
    }
#endif
    p_stk_data->OSFree = free * sizeof(OS_STK);           /* Compute number of free bytes on the stack */
    p_stk_data->OSUsed = (size - free) * sizeof(OS_STK);  /* Compute number of bytes used on the stack */
    return (OS_ERR_NONE);
}
#endif
/*$PAGE*/
/*

/*
*********************************************************************************************************
*                                            TASK STACK DATA
*********************************************************************************************************
*/

#if OS_TASK_CREATE_EXT_EN > 0
typedef struct os_stk_data {
    INT32U  OSFree;                    /* Number of free bytes on the stack                            */
    INT32U  OSUsed;                    /* Number of bytes used on the stack                            */
} OS_STK_DATA;
#endif

#if OS_TASK_STAT_EN > 0

void OS_TaskStat (void *p_arg) {……};  //这个就是守护进程

#endif

/*
*********************************************************************************************************
*                                            STATISTICS TASK
*
* Description: This task is internal to uC/OS-II and is used to compute some statistics about the
*              multitasking environment.  Specifically, OS_TaskStat() computes the CPU usage.
*              CPU usage is determined by:
*
*                                          OSIdleCtr
*                 OSCPUUsage = 100 * (1 - ------------)     (units are in %)
*                                         OSIdleCtrMax
*
* Arguments  : parg     this pointer is not used at this time.
*
* Returns    : none
*
* Notes      : 1) This task runs at a priority level higher than the idle task.  In fact, it runs at the
*                 next higher priority, OS_TASK_IDLE_PRIO-1.
*              2) You can disable this task by setting the configuration #define OS_TASK_STAT_EN to 0.
*              3) You MUST have at least a delay of 2/10 seconds to allow for the system to establish the
*                 maximum value for the idle counter.
*********************************************************************************************************
*/

#if OS_TASK_STAT_EN > 0
void  OS_TaskStat (void *p_arg)
{
    INT32U     run;
    INT32U     max;
    INT8S      usage;
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0;
#endif



    p_arg = p_arg;                               /* Prevent compiler warning for not using 'parg'      */
    while (OSStatRdy == OS_FALSE) {
        OSTimeDly(2 * OS_TICKS_PER_SEC / 10);    /* Wait until statistic task is ready                 */
    }
    max = OSIdleCtrMax / 100L;
    for (;;) {
        OS_ENTER_CRITICAL();
        OSIdleCtrRun = OSIdleCtr;                /* Obtain the of the idle counter for the past second */
        run          = OSIdleCtr;
        OSIdleCtr    = 0L;                       /* Reset the idle counter for the next second         */
        OS_EXIT_CRITICAL();
        if (max > 0L) {
            usage = (INT8S)(100L - run / max);
            if (usage >= 0) {                    /* Make sure we don't have a negative percentage      */
                OSCPUUsage = usage;
            } else {
                OSCPUUsage = 0;
            }
        } else {
            OSCPUUsage = 0;
            max        = OSIdleCtrMax / 100L;
        }
        OSTaskStatHook();                        /* Invoke user definable hook                         */
#if (OS_TASK_STAT_STK_CHK_EN > 0) && (OS_TASK_CREATE_EXT_EN > 0)
        OS_TaskStatStkChk();                     /* Check the stacks for each task                     */
#endif
        OSTimeDly(OS_TICKS_PER_SEC / 10);        /* Accumulate OSIdleCtr for the next 1/10 second      */
    }
}
#endif
/*$PAGE*/

#if OS_TASK_STAT_EN > 0

void OSStatInit (void) {……};  //每一个进程都得调用它

#endif

/*
*********************************************************************************************************
*                                        STATISTICS INITIALIZATION
*
* Description: This function is called by your application to establish CPU usage by first determining
*              how high a 32-bit counter would count to in 1 second if no other tasks were to execute
*              during that time.  CPU usage is then determined by a low priority task which keeps track
*              of this 32-bit counter every second but this time, with other tasks running.  CPU usage is
*              determined by:
*
*                                             OSIdleCtr
*                 CPU Usage (%) = 100 * (1 - ------------)
*                                            OSIdleCtrMax
*
* Arguments  : none
*
* Returns    : none
*********************************************************************************************************
*/

#if OS_TASK_STAT_EN > 0
void  OSStatInit (void)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr = 0;
#endif



    OSTimeDly(2);                                /* Synchronize with clock tick                        */
    OS_ENTER_CRITICAL();
    OSIdleCtr    = 0L;                           /* Clear idle counter                                 */
    OS_EXIT_CRITICAL();
    OSTimeDly(OS_TICKS_PER_SEC / 10);            /* Determine MAX. idle counter value for 1/10 second  */
    OS_ENTER_CRITICAL();
    OSIdleCtrMax = OSIdleCtr;                    /* Store maximum idle counter count in 1/10 second    */
    OSStatRdy    = OS_TRUE;
    OS_EXIT_CRITICAL();
}
#endif
/*$PAGE*/
/*

3.      怎样使用系统API

Task_42 (void *p_arg) {

    error_t error;

    OSStatInit(); //这里应该是CCTV所描写叙述的最美好的天朝。请放我在这里

    while(1) {

        switch(num) { 

             case 42:
                 printf(“42 is the answer to life, the universe, and everything\n”);
                 break;

             ......
             default:
                 break;
        }
    }
}

4.      使用

注意进程控制块的数据结构

/*
*********************************************************************************************************
*                                          TASK CONTROL BLOCK
*********************************************************************************************************
*/

typedef struct os_tcb {
    OS_STK          *OSTCBStkPtr;      /* Pointer to current top of stack                              */

#if OS_TASK_CREATE_EXT_EN > 0
    void            *OSTCBExtPtr;      /* Pointer to user definable data for TCB extension             */
    OS_STK          *OSTCBStkBottom;   /* Pointer to bottom of stack                                   */
    INT32U           OSTCBStkSize;     /* Size of task stack (in number of stack elements)             */
    INT16U           OSTCBOpt;         /* Task options as passed by OSTaskCreateExt()                  */
    INT16U           OSTCBId;          /* Task ID (0..65535)                                           */
#endif

    struct os_tcb   *OSTCBNext;        /* Pointer to next     TCB in the TCB list                      */
    struct os_tcb   *OSTCBPrev;        /* Pointer to previous TCB in the TCB list                      */

#if OS_EVENT_EN	|| (OS_FLAG_EN > 0)
    OS_EVENT        *OSTCBEventPtr;    /* Pointer to event control block                               */
#endif

#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
    void            *OSTCBMsg;         /* Message received from OSMboxPost() or OSQPost()              */
#endif

#if (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
#if OS_TASK_DEL_EN > 0
    OS_FLAG_NODE    *OSTCBFlagNode;    /* Pointer to event flag node                                   */
#endif
    OS_FLAGS         OSTCBFlagsRdy;    /* Event flags that made task ready to run                      */
#endif

    INT16U           OSTCBDly;         /* Nbr ticks to delay task or, timeout waiting for event        */
    INT8U            OSTCBStat;        /* Task      status                                             */
    INT8U            OSTCBStatPend;    /* Task PEND status                                             */
    INT8U            OSTCBPrio;        /* Task priority (0 == highest)                                 */

    INT8U            OSTCBX;           /* Bit position in group  corresponding to task priority        */
    INT8U            OSTCBY;           /* Index into ready table corresponding to task priority        */
#if OS_LOWEST_PRIO <= 63
    INT8U            OSTCBBitX;        /* Bit mask to access bit position in ready table               */
    INT8U            OSTCBBitY;        /* Bit mask to access bit position in ready group               */
#else
    INT16U           OSTCBBitX;        /* Bit mask to access bit position in ready table               */
    INT16U           OSTCBBitY;        /* Bit mask to access bit position in ready group               */
#endif

#if OS_TASK_DEL_EN > 0
    INT8U            OSTCBDelReq;      /* Indicates whether a task needs to delete itself              */
#endif

#if OS_TASK_PROFILE_EN > 0
    INT32U           OSTCBCtxSwCtr;    /* Number of time the task was switched in                      */
    INT32U           OSTCBCyclesTot;   /* Total number of clock cycles the task has been running       */
    INT32U           OSTCBCyclesStart; /* Snapshot of cycle counter at start of task resumption        */
    OS_STK          *OSTCBStkBase;     /* Pointer to the beginning of the task stack                   *//* 这里是栈基址*/
    INT32U           OSTCBStkUsed;     /* Number of bytes used from the stack                          *//* 这里是使用量*/
#endif

#if OS_TASK_NAME_SIZE > 1
    INT8U            OSTCBTaskName[OS_TASK_NAME_SIZE];
#endif
} OS_TCB;

比如例如以下两个进程的控制块:

1.栈溢出的进程如图1所看到的:

OSTCBStkSize   =  100(100是如果的值,实际值如图所看到的是256)(怎么解决,把该进程OSTCBStkSize设置为大于840/4。比方256 OK)

OSTCBStkUsed  = 840(注意这里单位是char型。所以须要除4之后与OSTCBStkSize比較)

两者的单位都是INT32U型,OSTCBStkUsed > OSTCBStkSize

OMG!!!     StackOverFlow (Id of Mine in stackOverFlow is linpeng1577@gmail.com )

 操作系统栈溢出检測之ucosII篇

                                                                                               图1

2.栈正常的进程如图2所看到的:

注意进程控制块

OSTCBStkSize   = 512

OSTCBStkUsed  = 136(136/4 = 34 < 512)

两者的单位都是INT32U型,OSTCBStkUsed < OSTCBStkSize

OMG!!! StackOverFlow?

 NO! (Id of Mine in stackOverFlow is linpeng1577@gmail.com )

操作系统栈溢出检測之ucosII篇

                                                                                              图2

转载请注明出处,

blog of linpeng1577

谢谢

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/116178.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • sql将截断字符串或二进制数据. 语句已终止_数据库将截断字符串

    sql将截断字符串或二进制数据. 语句已终止_数据库将截断字符串该错误一般出现在插入和修改数据的操作中,由于数据长度超过了数据库字段设定的长度,会出现此错误。在删除数据时也有可能由于触发器的原因导致此错误。解决办法即: 查找相应的字段,上传的插入的某个字段如果过长那么很有可能就是这个字段产生错误。将这个字段长度改为varchar类型,长度加长即可!

  • SpringCloud2.0

    SpringCloud2.0

  • Mysql decimal(m,d)的说明

    Mysql decimal(m,d)的说明看了一些博客,觉得很多都是复制的,不如自己亲测一篇:createtabledecimal_test(idintauto_incrementPRIMARYkey,scoredecimal(5,2)–取值范围是-999.99到999.99);–整数的位数必须小于等于m-d,不然报错。小数的位数可以大于d位。多…

  • 分布式事务框架Seata

    分布式事务框架Seata分布式事务中常见的三种解决方案目录一、分布式事务前奏 二、柔性事务解决方案架构 (一)、基于可靠消息的最终一致性方案概述 (二)、TCC事务补偿型方案 (三)、最大努力通知型 三、基于可靠消息的最终一致性方案详解 (一)、消息发送一致性 (二)、保证消息一致的变通做法 (三)、常规MQ消息处理流程和特点 (四)、消息重复发送问题和业务接口幂等性设计 (五)、本地消息服务方案 (六)、独立消息服务方案 (七)、消息服务子系统的设计实现 一、分布式事务

    2022年10月27日
  • V-rep学习笔记:切削

    V-rep学习笔记:切削

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号