最近在讀<程序員的自我修養:連接 裝載與庫>.如下是第十章的學習筆記:html
棧幀
那先有個問題,什麼是棧?程序員
在數據結構中, 棧是限定僅在表尾進行插入或刪除操做的線性表。棧是一種數據結構,它按照後進先出的原則存儲數據,先進入的數據被壓入棧底,最後的數據在棧頂,須要讀數據的時候從棧頂開始彈出數據。
複製代碼
在計算機系統中,棧也能夠稱之爲棧內存是一個具備動態內存區域,存儲函數內部(包括main函數)的局部變量和方法調用和函數參數值,是由系統自動分配的,通常速度較快;存儲地址是連續且存在有限棧容量,會出現溢出現象程序能夠將數據壓入棧中,也能夠將數據從棧頂彈出。壓棧操做使得棧增大,而彈出操做使棧減少。
棧用於維護函數調用的上下文,離開了棧函數調用就無法實現。
複製代碼
而什麼是棧幀(Stack Frame)呢? 每一次函數的調用,都會在調用棧(call stack)上維護一個獨立的棧幀(stack frame).每一個獨立的棧幀通常包括:bash
- 函數的返回地址和參數
- 臨時變量: 包括函數的非靜態局部變量以及編譯器自動生成的其餘臨時變量
- 函數調用的上下文 棧是從高地址向低地址延伸,一個函數的棧幀用ebp 和 esp 這兩個寄存器來劃定範圍.ebp 指向當前的棧幀的底部,esp 始終指向棧幀的頂部;
ebp 寄存器又被稱爲幀指針(Frame Pointer);
esp 寄存器又被稱爲棧指針(Stack Pointer);
在函數調用的過程當中,有函數的調用者(caller)和被調用的函數(callee). 調用者須要知道被調用者函數返回值; 被調用者須要知道傳入的參數和返回的地址;
函數調用
函數調用分爲如下幾步:數據結構
- 參數入棧: 將參數按照調用約定(C 是從右向左)依次壓入系統棧中;
- 返回地址入棧: 將當前代碼區調用指令的下一條指令地址壓入棧中,供函數返回時繼續執行;
- 代碼跳轉: 處理器將代碼區跳轉到被調用函數的入口處;
- 棧幀調整:
1.將調用者的ebp壓棧處理,保存指向棧底的ebp的地址(方便函數返回以後的現場恢復),此時esp指向新的棧頂位置; push ebp
2.將當前棧幀切換到新棧幀(將eps值裝入ebp,更新棧幀底部), 這時ebp指向棧頂,而此時棧頂就是old ebp mov ebp, esp
3.給新棧幀分配空間 sub esp, XXX
函數返回
函數返回分爲如下幾步:函數
- 保存被調用函數的返回值到 eax 寄存器中
mov eax, xxx
- 恢復 esp 同時回收局部變量空間
mov ebp, esp
- 將上一個棧幀底部位置恢復到 ebp
pop ebp
- 彈出當前棧頂元素,從棧中取到返回地址,並跳轉到該位置
ret
到這裏棧幀以及函數的調用與返回已經結束了,這裏涉及一些彙編的知道,這裏尚未記錄不一樣平臺的調用約定和一些特殊的寄存器.學習
參考
- 程序員的自我修養—連接、裝載與庫. 第10章
- https://www.cnblogs.com/dwlsxj/p/Stack.html
- 彙編原理