這周在網易云課堂上學習了《linux內核分析》——計算機是是如何工作的。本周學習內容有存儲計算機工作模型、x86會變基礎以及通過反編譯一段簡單的C語言的源代碼,初步接觸了計算機的工作過程。這里通過反匯編一個簡單的C程序,分析匯編代碼理解計算機是如何工作的。
先貼上一張對一個簡單的C語言程序反匯編后的匯編代碼的圖片:
這段匯編代碼是反編譯后去除了以 ".“ 開頭的行后的匯編代碼。
代碼中 %ebp 是棧底指針,%esp 是棧頂指針,%eax默認是存儲當前函數的返回值。
操作 pushl 是壓棧操作,popl 是彈棧操作,movl 是賦值操作,add 是加操作,sub 是減操作,call 是調用函數操作,leave 是銷棧操作,ret 是返回操作。
接下來分析下整個程序執行過程:
因為程序都是從 main 函數開始執行的,所以eip 首先指到第17行 main 函數的地址,并將 eip 的值壓到某個棧中,然后執行本程序 main.c。
main 函數首先將 %ebp 所指向的地址壓入調用 main 函數的棧中( pushl %ebp )。然后將棧頂的值賦值給棧底( movl %esp, %ebp ),也就意味著形成了一個新的空棧( 用于執行main函數中的操作 )。之后將棧頂往下減4個字節(32位)( subl $4, %esp ),然后形成了一個新的棧頂以及一個新的棧頂存儲空間。之后將值4賦值到棧頂( molv $4, (%esp) )。然后進行函數的調用操作( call f )。call 操作將當前行的下一行地址壓入棧并將 eip 指向函數 f 的地址。當調用完成后執行加操作( addl %7, %eax )。 接下來進行leave 操作。leave 操作將棧底指針賦值給棧頂( 將執行 main 的棧清空 )并將棧頂存儲的上一個棧的棧底地址賦值給 %ebp 并彈出該值,這也就意味著當前執行函數 main 的這個棧已經被銷毀并且回到調用 main 函數的那個棧中。最后有一個返回操作( ret ),將棧中存儲的 eip 的值賦值給當前 eip,其指向調用 main 函數的下一行。函數 main 執行完畢。
接下來的 f 函數和 g 函數也進行相似的操作。
因為 g 函數中沒有對棧頂進行操作,棧頂與棧底指向同一地址,所以就不用 leave 操作先將棧底的地址賦值給棧頂再移除棧底,直接將棧底彈出即可。
每個函數在執行過程中,都是先建立用于執行本函數的棧,然后執行操作,最后銷毀棧后返回。并不是所有操作都是對本函數的棧進行操作。
總結:
當今的計算機基本上都是用的馮諾依曼體系結構,通過總線將 CPU 與內存連接在一起,CPU 從內存中相應地址區域調取指令并執行。計算機執行命令用到最多的結構是棧結構,將命令執行結果壓入棧中,以備以后命令調取和返回。
對于單任務計算機,計算機順序執行所有命令。每個程序都要等到上一個程序執行完畢后才能執行。多任務計算機更多的是多個程序同時運行,同一個核心以極快的速度在多個不同的程序之間進行跳躍,每次只執行很少的命令,這樣看起來像在同時進行一樣。
從目前所學的知識來看,計算機所執行的操作就有三個,分別是 addl, subl, movl。push,pop,call,leave,ret 等操作都是用基礎的那三個操作組合而成的。計算機通過基礎的三個命令執行所有操作,高效便捷。
注:本人第一次寫此類博客,寫的并不是很好,如有不足之處請指出,我虛心求教,謝謝各位!
作者:李若森
原創作品轉載請注明出處
參考課程:《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000
新聞熱點
疑難解答