Linux內核分析(一)

Linux內核分析 —— 【實驗一:棧與程序 】

棧,是一種數據結構,是一種有限制的一維線性表。它的特點是“先進後出”,就像是一條只有一人寬的死衚衕,先進衚衕的人必須等後進衚衕的人全部離開後,才能離開。
但棧與程序之間又有什麼聯繫呢?

首先,看一段簡單的C程序代碼:test1.c

    int g(int x)
    {
      return x + 2;
    }
    int f(int x)
    {
      return g(x);
    }  
    int main(void)
    {
      return f(5) + 8;
    }

1) 在Linux 64位系統環境下,用以下命令將上述代碼進行反彙編:
gcc –S –o test1.s test.c -m32

如下圖如示
1-1

2)用gedit軟件打開test1.s文件:

1-4
1-3
1-2
可以看到三個函數g,f,main對應的彙編代碼,下面分析這個程序在棧中是如何執行的。

3)我們都知道main函數是程序的入口,所以先看main函數。注意:test1.s文件中以 ’ . ‘開頭的都是編譯器所添加的,可以忽略。

程序在內存中棧的增長方式如下:
1-5
每一個函數在調用的時候都會在內存中(也就是棧中)開闢一塊屬於自己的臨時空間,稱爲一個棧幀,它用於存儲該函數運行過程中的臨時變量,在函數調用結束後就會清除。程序在執行main函數時,棧的變化爲:
1-6
上圖(1)中沒有標示出ebp的位置,但我們知道它在更高的內存位置上。push ebp 就是把這個位置保存起來,以便於在修改ebp指針後,仍然可以恢復原來的值。
(2)中將當前esp位置賦給了ebp,也就是說棧底指針往下內存地址方向移了一段距離,和棧頂指針在同一位置。
(3)相當於push 5。5 是 f 函數的參數,c 程序函數參數的傳遞是通過棧來保存的。
(4)調用 f 函數。call f 可以看作是兩條指令:push eip和jmp f 。把將要執行的指令地址ret1入棧,然後跳轉到 f 函數,實現函數的調用。

1-7
(5)保存ebp1,把參數傳給將要調用的函數g。
(6)調用函數g 。
1-8
(7)中綠色代表main函數的棧幀,藍色是f函數的棧幀,黃色是g函數的棧幀。
(8)函數返回時,通常eax寄存器中的值作爲返回值。語句”movl 8(%ebp),%eax” 和”addl $2 , %eax” 這表示將參數的值加2賦給eax,即eax=7。

1-9
(9)(10)中ret 相當於pop eip 。將棧中保存的返回地址賦給eip,使得可以正常返回到原來的調用者f中,而返回值保存在eax寄存器中。
(11)同理,f函數在執行後,將結果保存到eax返回到main函數。在f函數彙編代碼中有一條“leave”語句,它相當於“mov ebp,esp”和”pop ebp”兩條語句。
(12)在main函數中,語句”addl $8,%eax“則表示將f函數的返回值加上8,結果爲eax=15 。再執行一次”leave“棧就被清空了。main函數也是函數,它也有它的調用者,只是我們看不到它的調用者而已,但它也需要”ret“返回到調用者,到這我們的程序就算結束了。

通過以上的例子,我們可以知道棧在程序執行過程中發揮着至關重要的作用,棧這種特殊的數據結構爲函數的調用提供了可能,使得程序的執行更加靈活、過程更加直觀;也使得編程更加簡單、方便;同時,只有理解棧的結構和性質,才能理解程序是如何執行的。


=========== 王傑 原創作品轉載請註明出處==============
《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章