日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入理解任务堆栈

發布時間:2023/12/15 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解任务堆栈 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

先來看這一個小函數,猜猜他的運行結果(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,堆?;刂?#xff0c;TCB指針其實指向同一塊內存。 創建任務時要指定任務大小,分配堆??臻g其實是一個特殊的malloc函數,他從堆??臻g分配,而不是從系統空間分配內存。任務堆棧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是相對橫的的。

?

總結

以上是生活随笔為你收集整理的深入理解任务堆栈的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。