中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

系統(tǒng)棧的工作原理

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

1.開篇

本篇文章著重寫的是系統(tǒng)中棧的工作原理,以及函數(shù)調(diào)用過程中棧幀的產(chǎn)生與釋放的過程,有可能名字過大,如果不合適我可以換一個名字,希望大家能夠指正,小丁虛心求教!如果有哪里寫的不清楚的或者錯誤的地方請及時更正,小丁再次謝過了。文章里面有錯別字,也可能會有好友說寄存器的32、16位的區(qū)別其實我感覺這里主要講的還是些原理性的東西,后續(xù)會將文章圖片錯別字進(jìn)行調(diào)整.

2.內(nèi)存的不同用途

根據(jù)不同的操作系統(tǒng),一個進(jìn)程可能被分配到不同的內(nèi)存區(qū)域去執(zhí)行。但是不管什么樣的操作系統(tǒng)、什么樣的計算機(jī)架構(gòu),進(jìn)程使用的內(nèi)存都可以按照功能大致分為以下4個部分:

(1)代碼區(qū):這個區(qū)域存儲著被裝入執(zhí)行的二進(jìn)制機(jī)器代碼,處理器會到這個區(qū)域取指并執(zhí)行。

(2)數(shù)據(jù)區(qū):用于存儲全局變量等。

(3)堆區(qū):進(jìn)程可以在堆區(qū)動態(tài)地請求一定大小的內(nèi)存,并在用完之后歸還給堆區(qū)。動態(tài)分配和回收是堆區(qū)的特點。

(4)棧區(qū):用于動態(tài)地存儲函數(shù)之間的關(guān)系,以保證被調(diào)用函數(shù)在返回時恢復(fù)到母函數(shù)中繼續(xù)執(zhí)行。

在Windows平臺下,高級語言寫出的程序經(jīng)過編譯鏈接,最終會變成PE文件。當(dāng)PE文件被裝載運(yùn)行后,就成了所謂的進(jìn)程。

PE文件代碼段中包含的二進(jìn)制級別的機(jī)器代碼會被裝入內(nèi)存的代碼區(qū)(.text),處理器將到內(nèi)存的這個區(qū)域一條一條地取出指令和操作數(shù),并送入運(yùn)算邏輯單元進(jìn)行運(yùn)算;如果代碼中請求開辟動態(tài)內(nèi)存,則會在內(nèi)存的堆區(qū)分配一塊大小合適的區(qū)域返回給代碼區(qū)的代碼使用;當(dāng)函數(shù)調(diào)用發(fā)生時,函數(shù)的調(diào)用關(guān)系等信息會動態(tài)地保存在內(nèi)存的棧區(qū),以供處理器在執(zhí)行完被調(diào)用函數(shù)的代碼時,返回母函數(shù)。

如果把計算機(jī)看成一個有條不紊的工廠,我們可以得到如下類比:

< CPU是完成工作的工人。

< 數(shù)據(jù)區(qū)、堆區(qū)、棧區(qū)等則是用來存放原料、半成品、成品等各種東西的場所。

< 存放在代碼區(qū)的指令則告訴CPU要做什么,怎么做,到哪里去領(lǐng)原材料,用什么工具來做,做完以后把成品放到哪個貨倉去。

< 值得一提的是,棧除了扮演存放原料、半成品的倉庫之外,它還是車間調(diào)度主任的辦公室。

3.棧與系統(tǒng)棧

從計算機(jī)科學(xué)的角度來看,棧指的是一種數(shù)據(jù)結(jié)構(gòu),是一種先進(jìn)后出的數(shù)據(jù)表。棧的最常見操作有兩種:壓棧(PUSH)、彈棧(POP);

用于標(biāo)識棧的屬性也有兩個:棧頂(TOP)、棧底(BASE)。

棧在內(nèi)存中的存放是高地址是棧底(Base),低地址是棧頂(Top)。

下面來演示下棧的工作原理:

首先我們先以這段匯編指令來進(jìn)行操作:

mov ax,0123H

push ax

mov bx 2244H

push bx

pop ax

pop bx

首先我們先將10000H-1000FH這段內(nèi)存空間來當(dāng)做棧來使用,首先執(zhí)行的操作是push ax,會將0123H壓入到棧中,SP=SP-2,SS:SP指向當(dāng)前棧頂當(dāng)前的單元,以當(dāng)前的單元為新的棧頂,將ax的數(shù)據(jù)送到SS:SP指向的內(nèi)存單元中,SS:SP此時指向新的棧頂。此時ax的數(shù)值是0123H;詳細(xì)請見下圖

接來下進(jìn)行第二部操作:push bx,操作同上;

接下來我們要演示的是pop操作,請注意pop操作的細(xì)節(jié),比如到了棧底的時候指針是在哪里?這些都是要進(jìn)行關(guān)注的。

CPU執(zhí)行pop ax時,SP=SP+2,SS:SP指向1000EH,pop操作棧頂元素,1000CH處的2266H依然存在,但是它在棧中不存在了,當(dāng)再次push等入棧指令后,SS:SP移至1000CH,并在里面寫入新的數(shù)據(jù),將其覆蓋。詳細(xì)看下圖操作:

當(dāng)再次進(jìn)行pop給bx時,這是SP=SP+2,這時候指針就超出了棧底,就變成了SP=10H,所以我們得出一個結(jié)論就是當(dāng)棧為空時,SS=1000H,SP=10H。詳細(xì)看下面操作:

內(nèi)存的棧區(qū)實際上指的就是系統(tǒng)棧。系統(tǒng)棧由系統(tǒng)自動維護(hù),它用于實現(xiàn)高級語言中函數(shù)的調(diào)用。對于類似C語言這樣的高級語言,系統(tǒng)棧的PUSH、POP等堆棧平衡細(xì)節(jié)是透明的。一般說來,只有在使用匯編語言開發(fā)程序的時候,才需要和它直接打交道。

4.函數(shù)調(diào)用約定與相關(guān)指令

函數(shù)調(diào)用約定描述了函數(shù)傳遞參數(shù)方式和棧幀同工作的技術(shù)細(xì)節(jié)。不同的操作系統(tǒng)、不同的語言、不同的編譯器在實現(xiàn)函數(shù)調(diào)用時的原理雖然基本相同,但具體的調(diào)用約定還是有差別的。這包括參數(shù)傳遞方式,參數(shù)入棧順序是從右向左還是從左向右,函數(shù)返回時恢復(fù)堆棧平衡的操作在子函數(shù)中進(jìn)行還是在母函數(shù)中進(jìn)行。
調(diào)用方式之間的差異

具體的,對于Visual C++來說,可支持以下3種函數(shù)調(diào)用約定:

如果要明確使用某一種調(diào)用約定,只需要在函數(shù)前加上調(diào)用約定的聲明即可,否則默認(rèn)情況下,VC會使用_stdcall的調(diào)用方式。 除了參數(shù)入棧方向和恢復(fù)棧平衡操作位置的不同之外,參數(shù)傳遞有時也會有所不同。例如,每一個C++類成員函數(shù)都有一個this指針,在Windows平臺中,這個指針一般是用ECX寄存器來傳遞的,但如果用GCC編譯器來編譯,這個指針會作為最后一個參數(shù)壓入棧中。

注意:同一段代碼用不同的編譯選項、不同的編譯器編譯鏈接后,得到的可執(zhí)行文件會有很多不同。

函數(shù)調(diào)用大概包括以下幾個步驟:

(1)參數(shù)入棧:將參數(shù)從右向左依次壓入系統(tǒng)棧中。

(2)返回地址入棧:將當(dāng)前代碼區(qū)調(diào)用指令的下一條指令地址壓入棧中,供函數(shù)返回時繼續(xù)執(zhí)行。

(3)代碼區(qū)跳轉(zhuǎn):處理器從當(dāng)前代碼區(qū)跳轉(zhuǎn)到被調(diào)用函數(shù)的入口處。

(4)棧幀調(diào)整:具體包括:

<1>保存當(dāng)前棧幀狀態(tài)值,已備后面恢復(fù)本棧幀時使用(EBP入棧)。

<2>將當(dāng)前棧幀切換到新棧幀(將ESP值裝入EBP,更新棧幀底部)。

<3>給新棧幀分配空間(把ESP減去所需空間的大小,抬高棧頂)。

<4>對于_stdcall調(diào)用約定,函數(shù)調(diào)用時用到的指令序列大致如下:

push 參數(shù)3????? ;假設(shè)該函數(shù)有3個參數(shù),將從右向做依次入棧

push 參數(shù)2

push 參數(shù)1

call 函數(shù)地址?? ;call指令將同時完成兩項工作:a)向棧中壓入當(dāng)前指令地址的下一個指令地址,即保存返回地址。 b)跳轉(zhuǎn)到所調(diào)用函數(shù)的入口處。

push ?ebp?????? ?;保存舊棧幀的底部

mov ?ebp,esp???? ;設(shè)置新棧幀的底部 (棧幀切換)

sub ??esp,xxx???? ;設(shè)置新棧幀的頂部 (抬高棧頂,為新棧幀開辟空間)

函數(shù)返回的步驟如下:

<1>保存返回值,通常將函數(shù)的返回值保存在寄存器EAX中。

<2>彈出當(dāng)前幀,恢復(fù)上一個棧幀。具體包括:

(1)在堆棧平衡的基礎(chǔ)上,給ESP加上棧幀的大小,降低棧頂,回收當(dāng)前棧幀的空間。

(2)將當(dāng)前棧幀底部保存的前棧幀EBP值彈入EBP寄存器,恢復(fù)出上一個棧幀。

(3)將函數(shù)返回地址彈給EIP寄存器。

<3>跳轉(zhuǎn):按照函數(shù)返回地址跳回母函數(shù)中繼續(xù)執(zhí)行。

還是以C語言和Win32平臺為例,函數(shù)返回時的相關(guān)的指令序列如下:

add esp,xxx???? ;降低棧頂,回收當(dāng)前的棧幀

pop ebp???????? ;將上一個棧幀底部位置恢復(fù)到ebp

retn??????????? ;a)彈出當(dāng)前棧頂元素,即彈出棧幀中的返回地址,至此,棧幀恢復(fù)到上一個棧幀工作完成。b)讓處理器跳轉(zhuǎn)到彈出的返回地址,恢復(fù)調(diào)用前代碼區(qū)

5.寄存器與函數(shù)棧幀

每一個函數(shù)獨占自己的棧幀空間。當(dāng)前正在運(yùn)行的函數(shù)的棧幀總是在棧頂。Win32系統(tǒng)提供兩個特殊的寄存器用于標(biāo)識位于系統(tǒng)棧頂端的棧幀。

(1)ESP:棧指針寄存器(extended stack pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向系統(tǒng)棧最上面一個棧幀的棧頂。

(2)EBP:基址指針寄存器(extended base pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向系統(tǒng)棧最上面一個棧幀的底部。

【寄存器對棧的標(biāo)識作用見(圖1)】

函數(shù)棧幀:ESP和EBP之間的內(nèi)存空間為當(dāng)前棧幀,EBP標(biāo)識了當(dāng)前棧幀的底部,ESP標(biāo)識了當(dāng)前棧幀的頂部。

在函數(shù)棧幀中,一般包含以下幾類重要信息。

(1)局部變量:為函數(shù)局部變量開辟的內(nèi)存空間。

(2)棧幀狀態(tài)值:保存前棧幀的頂部和底部(實際上只保存前棧幀的底部,前棧幀的頂部可以通過棧幀平衡計算得到),用于在本棧被彈出后恢復(fù)出上一個棧幀。

(3)函數(shù)返回地址:保存當(dāng)前函數(shù)調(diào)用前的“斷點”信息,也就是函數(shù)調(diào)用前的指令位置,以便在函數(shù)返回時能夠恢復(fù)到函數(shù)被調(diào)用前的代碼區(qū)中繼續(xù)執(zhí)行指令。

注:函數(shù)棧幀的大小并不固定,一般與其對應(yīng)函數(shù)的局部變量多少有關(guān)。函數(shù)運(yùn)行過程中,其棧幀大小也是在不停變化的。除了與棧相關(guān)的寄存器外,我們還需要記住另一個至關(guān)重要的寄存器。

EIP:指令寄存器(extended instruction pointer),其內(nèi)存放著一個指針,該指針永遠(yuǎn)指向下一條等待執(zhí)行的指令地址。 可以說如果控制了EIP寄存器的內(nèi)容,就控制了進(jìn)程——我們讓EIP指向哪里,CPU就會去執(zhí)行哪里的指令。這里不多說EIP的作用,我個人認(rèn)為王爽老是的匯編里面講EIP講的已經(jīng)是挺好的了~這里不想多寫關(guān)于EIP的事情。

6.結(jié)束語

本文是針對上面兩篇文章的一個基礎(chǔ)性的補(bǔ)充~希望大家能夠喜歡和指正其中的不足之處,小丁虛心學(xué)習(xí)于請教~不知道名字叫啥~

內(nèi)容參考:0day安全:軟件漏洞分析技術(shù)(第2版)

標(biāo)簽: 安全 代碼 漏洞

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:一張圖告訴你:Android系統(tǒng)哪代強(qiáng)?

下一篇:2015年4月編程語言排行榜:Java回到第一