操作系统栈溢出检测之ucosII篇[通俗易懂]

操作系统栈溢出检测之ucosII篇[通俗易懂]操作系统栈溢出检测之uc/osII篇Author              :     DavidLin(林鹏)E-mail               :       linpeng1577@gmail.com                                    linpeng1577@163.com 158820224344@163.co

大家好,又见面了,我是你们的朋友全栈君。

操作系统栈溢出检测之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 > 0
void  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/140801.html原文链接:https://javaforall.cn

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

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

(0)


相关推荐

发表回复

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

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