棧幀總結
任一時刻, 計算機都在執行乙個程式 (程序), 這個正在執行的狀態可以用以下內容唯一確定:
知道了以上三者, 可以唯一確定現在計算機正在執行哪個程式, 執行的是該程式的哪乙個函式的哪一行.
由於程式的**一般是不變的, 所以乙個程式的多個例項如果同時執行(同一程式的多個程序), 那麼它們共享同乙個程式**, 但是每乙個程序都有自己各自的暫存器組狀態和堆疊資料. 同理, 同一程序的多個執行緒擁有各自的暫存器組狀態和棧, 共享同乙個程序的堆疊資料.
本專題以 ia-32 架構為語境.名稱
用法%eax
隨便用%ebx
隨便用, 使用完要恢復初值
%ecx
隨便用%edx
隨便用%esi
隨便用, 使用完要恢復初值
%edi
隨便用, 使用完要恢復初值
%esp
指向棧頂, 表示當前棧幀的上界
%ebp
表示當前棧幀的下界, 即函式的作用域
%eip
棧幀即為棧 (記憶體) 的某一部分, 這一部分唯一對應乙個函式的作用域, 它裡面的資料是該函式的所有區域性變數和要呼叫下乙個函式時傳遞的引數以及一些需要臨時儲存的資料. 每呼叫乙個函式, 棧上便會建立乙個該函式對應的棧幀.
在學c語言的時候, 看到過這句話:
函式區域性變數的生命週期從執行函式開始, 到函式返回結束用底層的語言來解釋就是: 執行函式的時候建立棧幀 (裡面包含函式的區域性變數), 函式返回時釋放棧幀.
由於 (%esp, %ebp) 可以唯一確定乙個棧幀, 而相鄰棧幀的上下界可以互相推導出來, 所以每呼叫乙個函式, 都會先儲存之前棧幀的下界 %ebp, 然後再給當前棧幀的上界 %esp 賦新的值.
下面來詳細解釋上述的這幾段話.
將以下c語言**
void
foo(
int p1,
int p2)
用gcc和objdump轉化為組合語言
push %ebp
mov %esp,
%ebp
sub $0x10
,%esp
movl $0x7,-
0xc(
%ebp)
mov 0x8
(%ebp)
,%eax
mov %eax,
-0x8
(%ebp)
mov 0xc
(%ebp)
,%eax
add $0x1
,%eax
mov %eax,
-0x4
(%ebp)
一行一行來解釋:
將%ebp壓棧, 即儲存呼叫者的棧幀的下界 ( 棧幀的上界是呼叫該語句前的%esp, 因為壓棧導致 %esp 的值 -4 )
將 %esp 的值賦給 %ebp, 即當前棧幀的下界是上乙個棧幀的上界 + 4. ( 因為棧幀是連續儲存的, 所以只需儲存上乙個棧幀的起點即可, 上乙個棧幀的終點可以由當前棧幀的起點計算出來. )
將 %esp - 0x10, 即為當前棧幀分配空間, 我們發現當前函式實際上只有3個臨時變數, 需要的空間是 12 byte 小於分配的 16 byte, 這是因為要進行對齊.
將立即數7賦值給 %ebp - 12 的位置
將 %ebp + 8 位置的值賦給中間變數
將中間變數賦值給 %ebp - 8的位置
將 %ebp + 12 位置的值賦值給中間變數
中間變數 + 1
將中間變數的值賦值給 %ebp - 4 的位置
前三行留給下文具體解釋, 我們只關注 4 ~ 9 行, 很明顯可以看到函式引數和區域性變數的位置可以由 %ebp 來計算得到, 如下圖所示.
由此可見, 通過 %ebp 可以得到當前函式的所有變數以及引數的位置, 所以 %ebp 就相當於乙個函式的基位址. 而下乙個棧幀的 %ebp 是由 %esp 得到的, 所以 %esp 的值即為當前棧幀和下乙個棧幀的分界線. 因此%esp 和 %ebp 定義了乙個棧幀.
就是這樣.
計算機世界就是現實世界的延伸
作業系統排程演算法分為以下幾種 1 先來先服務,2 優先順序排程,3 短作業優先排程,4 輪轉法排程,5 高響應比優先排程。這五種演算法其實就是現實生活中的活生生的管理方法。1,大家都是平民,則先來的人先用公廁 先來先服務。2,如果來人中有乙個是市長呢,就要讓市長先上。這就是 優先順序排程。3,後面...
CSAPP 1 計算機系統的漫遊
首先,儲存與計算機上的資訊都是位,計算機通過上下文判斷儲存資訊的型別 文字檔案 二進位制檔案 1.c程式格式的流轉 0 hello.c 通過預處理器 cpp 增加標頭檔案後得到hello.i 0 hello.i通過編譯器編譯 ccl 得到hello.s 匯程式設計序 0 hello.s通過彙編器 a...
活著的意義是什麼? 關於世界觀的一些看法
夜深人靜,長夜漫漫,我卻與睡眠無緣。與其與失眠的痛苦搏鬥,不如來寫一下文章,抒發一下情感,順便提高一下自己的寫作水平,聊勝於無。首先,我想提出乙個問題 我們活著的意義到底是什麼?其次,提前說一下 對於這個問題的答案,我像大部分人一樣根本摸不到頭腦,一下文章是我對世界的一些見解,或許有些負能量,如若不...