大家好,又见面了,我是你们的朋友全栈君。
操作系统栈溢出检测之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 )
图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/140801.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...