您的位置:首页 > 其它

深入理解任务堆栈

2012-02-24 00:27 141 查看
先来看这一个小函数,猜猜他的运行结果(VC6环境)?

#include <stdio.h>

void b()

{

int data[10];

printf("helloworld!/r/n");

data[11]-=5;

}

int main()

{

b();

return 0;

}

堆栈溢出,肯定不正常,马上有人叫起来了。

没错, 那么结果是什么呢,为什么会不停打印helloworld呢,我们将用堆栈揭开他的奥秘。

且看main函数汇编代码。

很简单, L12 调用b函数, L13对返回值赋0.

这里有个很关键的东东: call

call包含2部分操作,call的下一条指令地址入栈,跳转,也就是从效果来说,包含push 0040108D 和 jmp 00401005两条操作。 假如,你打开内存窗口,你会看到,堆栈里已经有0040108D 这个值了。

10: int main()

11: {

...........

12: b();

00401088 call @ILT+0(b) (00401005)

13: return 0;

0040108D xor eax,eax

14: }

再来看函数b

当你把 printf("helloworld!/r/n"); 替换为 printf("%08x!/r/n",data[11]);时,你会发现,程序在不停的打印0040108D!, 显而易见,你修改的data[11]其实就是函数b的返回值地址,而data[11]
-= 5;更是巧妙的利用 call 00401005 这条指令正好是5个字节的特点,将返回地址正好修改到了 0040108D ,也就是说函数返回时会再次调用函数b。每次b()都会把返回值改为b返回的地址,导致b()被不停的调用。

为什么data[11]正好是函数的返回值呢,让我们来看堆栈和任务有和关系

任务(线程)都有一个堆栈,任务创建时创建,任务撤销时撤销。 任务的创建本质上包含2点。

1 任务资源的分配(任务TCB和任务堆栈),很多嵌入式操作系统把TCB和堆栈是分配在一起的,比如Vxworks操作系统,其任务ID,堆栈基地址,TCB指针其实指向同一块内存。 创建任务时要指定任务大小,分配堆栈空间其实是一个特殊的malloc函数,他从堆栈空间分配,而不是从系统空间分配内存。任务堆栈windows下默认比较大,嵌入式OS则比较小,经常64k左右。 而局部变量就保存在堆栈中,当访问局部变量越界时,就发生了我们常说的"堆栈被踩了",堆栈被踩得话后果严重,轻则导致某次运行结果不对(这种问题很难定位),重则导致程序崩溃,例如把上面程序改为data[11]-=4,则程序直接崩溃。

2 任务的初始化,包含2部分,任务TCB的初始化,并且把TCB和操作系统关联。

TCB中包含任务的很多东西, 比如任务拥有的信号量的链表,文件描述符的链表,CPU寄存器的值(任务切换时用的),任务优先级,堆栈地址,任务名称等等,这些都需要初始化。初始化完成之后,操作系统会把这个任务TCB假如调度队列,如果加入调度队列时任务状态是就绪,那么当他拿到CPU时就可以直接运行了。

堆栈中包含任务的栈帧,也就是说在函数调用链(A call B,B call C,C call D,D call E),那么堆栈中,ABCDE函数分别对应自己的一段栈帧。以E为例 E的栈帧包含A函数的传入参数,函数返回值,局部变量和临时保存的寄存器值。

函数栈帧在主调函数和被掉函数中分配,在函数返回时释放,这就是为什么局部变量地址在函数返回后其值可能失效。

例如 下面代码FuncB分配的函数栈帧在FuncB执行完后又被分配给FuncC,FuncC中很可能会踩到FuncB曾经的局部变量。

FuncA{

FuncB();

FuncC();

}

任务(线程)的栈以及上面函数b的栈为下图。





*debug版本的函数b其实除了data[10],还在局部变量位置分配了一部分内存用来做调试,不过我们不用关系他。

*为什么是data[11],而不是data[10]/data[12]或者其他? x86下编译器函数入口一般会有2条指令。

push ebp

move ebp,esp

其实就是将ebp作为帧指针来用(函数帧即为栈中一个函数所拥有的一段内存)。

而这样就可以在函数中采用ebp-XXX表示局部变量,用ebp+XXX来表示传入参数。 函数中经常会有一些push操作,

采用esp对局部变量和参数寻址远不如用ebp来的省事了,因为esp是经常变化的,而ebp是相对横的的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: