過(guò)程(函數(shù)調(diào)用的原理)
過(guò)程在高級(jí)語(yǔ)言中稱為函數(shù)或者方法,一個(gè)過(guò)程包括將數(shù)據(jù)和控制從代碼的一部分傳遞到另一部分。此外,它還必須在進(jìn)入時(shí)為過(guò)程中的局部變量分配空間,并在退出時(shí)釋放空間,大多數(shù)機(jī)器只提供了轉(zhuǎn)移控制到過(guò)程和從過(guò)程中轉(zhuǎn)移控制這種簡(jiǎn)單的指令。數(shù)據(jù)傳遞和局部變量的分配釋放都是通過(guò)操縱程序棧來(lái)實(shí)現(xiàn)。合理的構(gòu)建方法并調(diào)用,能大大增加代碼的復(fù)用性,也能是代碼結(jié)構(gòu)更加清晰。
要提供對(duì)過(guò)程的機(jī)器級(jí)支持,必須要處理許多不同的屬性。舉例,假設(shè)過(guò)程 P 調(diào)用過(guò)程 Q ,Q 執(zhí)行后返回 P 。這些動(dòng)作包括下面一個(gè)或多個(gè)機(jī)制:
傳遞控制:在進(jìn)入過(guò)程 Q? 的時(shí)候,程序計(jì)數(shù)器必須被設(shè)置為 Q 的代碼的起始地址,然后在返回的時(shí)候,要把程序計(jì)數(shù)器設(shè)置為 P 中調(diào)用 Q 后面那條指令的地址。
傳遞數(shù)據(jù):P 必須能夠向 Q 提供一個(gè)或多個(gè)參數(shù),Q 必須能夠向 P 返回一個(gè)值。
分配和釋放內(nèi)存:開始時(shí),Q 可能需要為局部變量分配空間,而在返回前,又必須釋放這些存儲(chǔ)空間。

程序用棧來(lái)管理它的過(guò)程所需要的存儲(chǔ)空間,棧和程序寄存器存放著傳遞控制和數(shù)據(jù),分配內(nèi)存所需的信息。棧在轉(zhuǎn)移控制和數(shù)據(jù)傳送過(guò)程中扮演重要角色。
下面以例子說(shuō)明函數(shù)調(diào)用的過(guò)程:
源代碼:下圖中swap_add函數(shù)交換指針xp和yp指向的兩個(gè)值,并返回兩個(gè)值得和。函數(shù)calller調(diào)用局部變量arg1和arg2的棧幀地址進(jìn)行計(jì)算返回結(jié)果。

匯編代碼:下圖可以看到calller代碼開始的地方把棧指針減少16($16代表立即數(shù)),實(shí)際上就是在棧上分配16個(gè)字節(jié),然后把$534分配到(%rsp)棧指針中,把$1057分配到(%rsp)+8的棧指針中,然后將兩個(gè)地址分別存儲(chǔ)到%rsi和%rdi中,調(diào)用call進(jìn)行計(jì)算。繼續(xù),將(%rsp)的值放到(%rdx)中,調(diào)用subq指令將(%rsp)加8的地址放到%rdx中計(jì)算兩個(gè)參數(shù)的差,imulq進(jìn)行求和運(yùn)算。然后將棧指針加上16釋放棧幀。函數(shù)調(diào)用完畢。

過(guò)程的實(shí)現(xiàn)主要就是在于數(shù)據(jù)如何在調(diào)用者和被調(diào)用者之間傳遞,以及在被調(diào)用者當(dāng)中局部變量?jī)?nèi)存的分配以及釋放。而過(guò)程實(shí)現(xiàn)當(dāng)中,參數(shù)傳遞以及局部變量?jī)?nèi)存的分配和釋放都是通過(guò)以上介紹的棧幀來(lái)實(shí)現(xiàn)的,大部分情況下,我們認(rèn)為過(guò)程調(diào)用當(dāng)中做了以下幾個(gè)操作(32位):
1、備份原有棧幀。調(diào)整當(dāng)前幀指針到棧指針的位置。
pushl? %ebp ;movl? %esp,%ebp
2、建立起來(lái)的棧幀就是為被調(diào)用者準(zhǔn)備的。當(dāng)被調(diào)用者使用棧幀時(shí),需要為局部變量分配預(yù)留內(nèi)存。
subl $16 ,%esp
3、備份被調(diào)用者保存的寄存器的值,如果有值得話,備份的方式就是壓入棧頂。
pushl? %ebx
4、使用建立好的棧幀。
5、恢復(fù)調(diào)用者寄存器中的值。
6、彈出返回地址,跳出當(dāng)前過(guò)程,繼續(xù)執(zhí)行調(diào)用者的代碼。
上面主要講了棧規(guī)則的機(jī)制,解決了數(shù)據(jù)如何在調(diào)用者和被調(diào)用者之間傳遞,以及在被調(diào)用者當(dāng)中局部變量?jī)?nèi)存的分配以及釋放。