當前位置:
首頁 >
程序是怎么运行的?
發布時間:2025/6/17
30
豆豆
程序是怎么運行的? 也許我們習慣了在IDE環境中敲代碼、執行程序,只需一個鍵就能完成從編譯、匯編、鏈接到顯示結果的所有工作。。
接下來我把它編譯成匯編代碼。注意:在Unix/Linux下使用的是AT&T格式的匯編語言,而Dos/Windows下是Intel風格的匯編。這兩者在語法格式上還是有很大的不同的。由于這里是在Linux系統上編譯的,所以產生的是AT&T格式的匯編。 將上面的代碼保存為main.c。下面對main.c進行編譯:gcc?–S?–o?main.s?main.c?-m32 這里的-S表示輸出匯編代碼(如果不加的話就直接生產目標文件了)。后面的-m32表示生成32位的匯編代碼。 執行這條命令后,產生了如下匯編代碼main.s: 注意:生成的匯編代碼會有一些其它的信息,這里我把它們刪了,我們只分析和我們的C代碼密切相關的部分。 我們知道,系統會為每個進程分配一個堆棧空間(關于進程的堆棧空間的詳細介紹請參考《Unix環境高級編程》): 程序中的變量包括函數調用占用的都是棧的空間,它的地址是向下遞減的。那么初始化棧假設是這樣的: 這里為了簡化問題,假設棧底地址為100,由于地址向下遞減,所以依次是96,92,88..... %ebp和%esp是寄存器,其中%ebp存放的是棧底指針,而%esp存放的是棧頂指針。 首先從main函數開始執行: pushl %ebp ? ? ? 表示把%ebp寄存器中的內容壓入堆棧,這時%esp就指向地址96處了(棧頂指針自動減4,因為是按32位尋址的。。)。 movl %esp,%ebp 表示將棧頂指針賦值給棧底指針,那么這時%ebp就是96了。 subl $24,%esp 將棧頂指針減去24,這時%esp指向72. movl $1,-12(%ebp) movl $2,-8(%ebp) 這兩條指令表示把立即數1和2分別放置到(%ebp)減去12和減去8的位置上。 movl -8(%ebp),%eax movl %eax,4(%esp) 表示把2賦給%eax,然后再放置在(%esp)+4的位置上。下面兩天指令也相同。發現沒?這里就是在給函數add()傳遞參數呢。。 此時,棧的情況如下: 接下來執行: call add call指令開始調用add函數,它的作用相當于下面兩天指令: pushl %eip
movel add %eip 其中%eip寄存器存放的是當前執行的指令的地址。這里調用了call以后,相當于把當前指令的地址壓入棧中,此時%esp指向68,然后把add函數要執行的指令的地址賦給%eip。 接下來開始執行add函數中的指令,流程和上面的一樣。 這時又用pushl %ebp把棧底指針入棧,你可能會有疑問,為什么每次調用一個函數都需要把%ebp入棧呢?其實你會發現,當add函數執行到后來,有一個popl %ebp指令,這個指令相當于把原來入棧的%ebp又恢復原值了,這個時候的%ebp指向的就是main函數的棧底了(相當于又回到main函數的環境了)。當然add函數里還有一個ret,ret指令代表popl %eip,就是把原來的%eip有恢復原值了,這樣代碼執行流就回到了main函數中調用add函數的下一條指令了。 當main函數執行完movl %eax,-4(%ebp)后,開始執行leave,leave指令相當于如下兩條指令: mov %ebp,%esp
pop %ebp 發現了嗎?這里相當于恢復調用main函數之前的棧情況了,借著執行ret后就回到原先的指令流了。這時候棧就恢復到最開始的狀態啦。。簡單吧?關于函數調用過程中棧空間的變化情況現在了解一點了嗎? posted on 2015-03-24 21:50 愚&道 閱讀(...) 評論(...) 編輯 收藏
那么你有沒有疑惑過,當你執行一個簡單的C程序時,它內部到底發生了什么呢?下面我們就從匯編語言的層面上來分析一下程序運行的全過程。
假設我寫了一個簡單的a+b的程序:接下來我把它編譯成匯編代碼。注意:在Unix/Linux下使用的是AT&T格式的匯編語言,而Dos/Windows下是Intel風格的匯編。這兩者在語法格式上還是有很大的不同的。由于這里是在Linux系統上編譯的,所以產生的是AT&T格式的匯編。 將上面的代碼保存為main.c。下面對main.c進行編譯:
movel add %eip 其中%eip寄存器存放的是當前執行的指令的地址。這里調用了call以后,相當于把當前指令的地址壓入棧中,此時%esp指向68,然后把add函數要執行的指令的地址賦給%eip。 接下來開始執行add函數中的指令,流程和上面的一樣。 這時又用pushl %ebp把棧底指針入棧,你可能會有疑問,為什么每次調用一個函數都需要把%ebp入棧呢?其實你會發現,當add函數執行到后來,有一個popl %ebp指令,這個指令相當于把原來入棧的%ebp又恢復原值了,這個時候的%ebp指向的就是main函數的棧底了(相當于又回到main函數的環境了)。當然add函數里還有一個ret,ret指令代表popl %eip,就是把原來的%eip有恢復原值了,這樣代碼執行流就回到了main函數中調用add函數的下一條指令了。 當main函數執行完movl %eax,-4(%ebp)后,開始執行leave,leave指令相當于如下兩條指令: mov %ebp,%esp
pop %ebp 發現了嗎?這里相當于恢復調用main函數之前的棧情況了,借著執行ret后就回到原先的指令流了。這時候棧就恢復到最開始的狀態啦。。簡單吧?關于函數調用過程中棧空間的變化情況現在了解一點了嗎? posted on 2015-03-24 21:50 愚&道 閱讀(...) 評論(...) 編輯 收藏
轉載于:https://www.cnblogs.com/yudao/p/4364124.html
總結
- 上一篇: 我对NHibernate的感受(2):何
- 下一篇: python 在 eclipse 上的