函數調用另一個詞語表示叫作 過程。一個過程調用包括將數據和控制從代碼的一部分傳遞到另一部分。另外,它還必須在進入時為過程的局部變量分配空間,並在推出時釋放這些空間。而數據傳遞,局部變量的分配和釋放通過操縱程序棧來實現。在了解本文章之前,您需要先對程序的進程空間有所了解,即對進程如何使用內存?如果你知道這些,下面的內容將是很easy的事情了。為了您的回顧還是將簡單的分布圖貼出來,便於您的回顧。
我們先來了解一個概念,棧幀(stack frame),機器用棧來傳遞過程參數,存儲返回信息,保存寄存器用於以后恢復,以及本地存儲。為單個過程(函數調用)分配的那部分棧稱為棧幀。棧幀其實是兩個指針寄存器,寄存器%ebp為幀指針,而寄存器%esp為棧指針,當程序運行時,棧指針可以移動(大多數的信息的訪問都是通過幀指針的)。總之簡單一句話,棧幀的主要作用是用來控制和保存一個過程的所有信息的。棧幀結構如下所示:
如果你已經對這個圖已經非常了解了,那么就沒有必要再看下去了。因為下面的內容都是對這幅圖的講解。
假設過程P(調用者)調用過程Q(被調用者),則Q的參數放在P的棧幀中。另外,當P調用Q時,P中的返回地址被壓入棧中,形成P的棧幀的末尾(返回地址就是當程序從Q返回時應該繼續執行的地方)。Q的棧幀從保存的幀指針的值開始,后面到新的棧指針之間就是該過程的部分了。
過程實例講解:
下面以這個程序為例進行簡要說明函數調用的基本過程。
int swap_add(int* xp,int* yp) { int x = *xp; int y = *yp; *xp = y; *yp = x; return x+y; } int caller(){ int arg1 = 534; int arg2 = 1057; int sum = swap_add(&arg1,&arg2); int diff = arg1 - arg2; return sum * diff; }
經過匯編之后caller部分的代碼如下:
caller: pushl %ebp //保存%ebp movl %esp,%ebp //設置新的幀指針為舊的棧指針 subl $24,%esp //分配24子節的棧空間 movl $534,-4(%ebp) //設置arg1=534 movl $1057,-8(%ebp) //設置arg2=1057 leal -8(%ebp),%eax //計算&arg2 movl %eax,4(%esp) //將&arg2存入棧中 leal -4(%ebp),%eax //計算&arg1 movl %eax,(%esp) //將&arg1存入棧中 call swap_add //調用swap_add
這段代碼先保存了%ebp的一個副本,將新的過程(該函數的ebp)的ebp設置為棧幀的開始位置。然后將棧指針減去24,從而在棧上分配了24字節的空間(你應該思考一下為什么是24字節),然后是初始化兩個局部變量,計算兩個局部變量的地址並存入棧中,形成了函數swap_add的參數。將這些參數存儲到相對於棧指針偏移量為0和+4的地方,留待稍后的swap_add調用訪問。然后調用swap_add.
接下的代碼是swap_add的函數部分:
swap_add: pushl %ebp movl %esp,%ebp pushl %ebx movl 8(%ebp),%edx movl 12(%ebp),%ecx movl (%edx),%ebx movl (%ecx),%eax movl %eax,(%edx) movl %ebx,(%ecx) addl %ebx,%eax popl %ebx popl %ebp ret
代碼分為3部分 建立部分:初始化棧幀;主體部分:執行過程的實體計算;結束部分:回復棧幀的狀態,以及過程返回。這一部分的代碼比較簡單,就不在一一介紹,根據以上的3部分,划分的已經很清晰了。(說明一點程序在執行到swap_add的代碼之前,也就是在執行call語句已經把返回地址壓入棧中)值得注意的是最后一部分的popl %ebx popl %ebp。它的作用是恢復了之前存儲的棧幀指針的值,也就是調用程序的原始棧幀指針。從而程序就可以得到返回(有些細心的人會問那返回值咋么辦?呵呵,返回值是存入了%eax中,在接下來的調用程序caller中直接訪問該寄存器就可以了)。
下面就是返回之后繼續執行的部分代碼了:
movl -4(%ebp),%edx subl -8(%ebp),%edx imull %edx,%eax leave ret
為了計算diff,從棧中取出arg1,和arg2的值,並將寄存器%eax當做swap_add的返回值。
整個過程的棧變化如下所示:
推薦一篇對棧幀的講解不錯的文章:http://blog.csdn.net/yxysdcl/article/details/5569351
參考文獻:《深入理解計算機系統》