大家好,又见面了,我是全栈君。
操作系统栈溢出检測之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 )
图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 )
图2
转载请注明出处,
谢谢
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/116178.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...