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

时间:2023-01-13 14:56:24

操作系统栈溢出检測之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!!!     * (Id of Mine in * is linpeng1577@gmail.com )

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGlucGVuZzEyMzU4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

图1

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

注意进程控制块

OSTCBStkSize   = 512

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

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

OMG!!! *?

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

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

图2

转载请注明出处,

blog of linpeng1577

谢谢