在進行本章的講解以前,咱們先說明講解的機器語言型號。上一篇彙編語言和機器語言咱們講過,機器語言是直接面向處理器(Processor:CPU)的程序設計語言,可是每一種這樣的微處理器(CPU)因爲硬件設計和內部結構的不一樣,因此每一種微處理器都有本身的機器指令集,也就是機器語言。而彙編語言是便於記憶的機器語言。本系列博客將會介紹兩種相關的機器語言:Intel IA32 和 x86-64。前者是當今大多數計算機的主導語言,然後者是在 64 位機器上運行的擴展,咱們先從 Intel IA32開始。html
前面咱們就說過,計算機系統使用了多種不一樣的抽象,利用更簡單的抽象模型來隱藏實現的細節。對於機器級編程來講,有兩種抽象特別重要:java
①、第一種是將機器級程序的格式和行爲定義爲指令集體系結構(Instruction set architecture ,ISA),它定義了處理器狀態、指令的格式,以及每條指令對狀態的影響。大多數 ISA,包括 Intel IA32 和 x86-64,將程序的行爲描述成好像每條指令是按順序執行的,即一條指令結束後,下一條指令開始。處理器的硬件遠比描述的精細複雜,它們併發的執行許多指令,可是能夠採起措施保證總體行爲與 ISA 指定的順序執行徹底一致。編程
②、第二種是機器程序使用的存儲器地址是虛擬地址,提供的存儲器模型看上去是一個很是大的字節數組。存儲器系統的實際實現是將多個硬件存儲器和操做系統軟件組合起來。數組
在整個編譯過程當中,編譯器會完成大部分工做,將把用 C 語言提供的相對比較抽象的執行模型表示的程序轉化成處理器執行的基本指令,也就是彙編語言,彙編語言在被彙編器轉化成機器語言,而後計算機去執行。彙編語言也就是具備更好的可讀性的機器語言,因此可以理解彙編代碼以及它與原始 C 代碼的關係,是理解計算機如何執行程序的關鍵步驟。架構
咱們在寫 C 程序時,處理器的狀態都是隱藏的,即咱們編碼不用去直接操做處理器。可是在彙編語言中,以下的幾個處理器狀態是可見的:併發
1、程序計數器(在 IA32 中一般稱爲 PC,用 %eip 表示):指示將要執行的下一條指令在存儲器中的地址。ide
2、整數寄存器文件:包含8個命名的位置,能夠存儲一些地址或者整數的數據。有的用來記錄某些重要的程序狀態,有的則用來保存臨時數據。函數
3、條件碼寄存器:保存最近執行的算數或邏輯指令的狀態信息,它們用來實現控制或數據流中的條件變化,好比用來實現 if 和 while 語句。優化
4、浮點寄存器:存儲浮點數。編碼
注意:C 語言提供的模型能夠在存儲器中聲明和分配各類數據類型的對象。可是實際上機器代碼則只是簡單的將存儲器當作是一個很大的、按字節尋址的數組。
彙編代碼不區分有符號或者無符號整數,不區分各類類型的指針。甚至不區分指針和整數。
程序存儲器包含程序的可執行機器代碼,操做系統須要的一些信息,用來管理過程調用和返回的運行時棧,以及用戶分配的存儲器塊。
程序存儲器用虛擬地址來尋址,在任意給定的時刻,只認爲有限的一部分虛擬地址是合法的。操做系統則負責管理虛擬地址空間,將虛擬地址翻譯成實際處理器存儲器(processor memory)中的物理地址。
以下這是一段 C 程序代碼 hello.c:
#include <stdio.h> int main() { return sum(1,3); } int accum = 0; int sum(int x,int y) { int t= x+y; accum += t; return t; }
而後執行以下命令生成彙編程序
gcc -O1 -S hello.c
-O1是優化選項,少優化->多優化:
O0 -->> O1 -->> O2 -->> O3
-O0表示沒有優化,-O1爲缺省值,-O3優化級別最高
生成的彙編程序 hello.s
.file "hello.c" .text .globl sum .type sum, @function //定義全局函數sum sum: .LFB12: .cfi_startproc leal (%rsi,%rdi), %eax //把寄存器%rsi和寄存器%rdi的值的地址裝入eax中,即&(rsi+rdi)=eax addl %eax, accum(%rip) //把寄存器%eax和寄存器%rip的值相加,並存放到 %rip中 ret .cfi_endproc .LFE12: .size sum, .-sum .globl main //主函數main .type main, @function main: .LFB11: .cfi_startproc movl $3, %esi //將數據3複製到%esi寄存器 movl $1, %edi movl $0, %eax call sum //將 sum 指令的地址壓入到棧中,也就是下一條指令執行調用 sum 函數 rep ret .cfi_endproc .LFE11: .size main, .-main .globl accum //定義全局變量accum .bss .align 4 .type accum, @object .size accum, 4 accum: .zero 4 .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-18)" .section .note.GNU-stack,"",@progbits
注意:全部以 ‘.’ 開頭的行都是指導彙編器和連接器的命令,咱們一般能夠忽略這些行。
如今這些彙編指令你們能夠不用徹底理解,後面會詳細進行講解。
因爲計算機是由16位體系結構擴展爲32位體系結構的,Intel 用術語 「字」(word) 表示16位數據類型,所以 32 位表示 「雙字」(double words),64 位數稱爲「四字」(quad words).
前面的彙編代碼咱們能夠看到全部的彙編指令都帶有字母 l,好比movl、addl、subl、pushl等等,這個l的後綴其實就是表示的數據格式,表示咱們操做的是32位的數值。
下面咱們看一下 C 語言基本數據類型對應的 IA32 表示:
上面的圖示很好理解,好比mov指令,它是一個數據傳送的指令,那麼movb就表明傳送一個字節的數據,movw就表明傳送兩個字節的數據,而movl就表明傳送四個字節的數據。須要注意的是,long long int在IA32架構中是不支持這種數據格式的。並且彙編代碼使用後綴 「l」 來表示 4 字節整數和8字節雙精度浮點數,這不會產生歧義,由於浮點數使用的是一組徹底不一樣的指令和寄存器。