CPU總是周而複始地做同一件事:從內存取指令,然後解釋執行它,然後再取下一條指令,再解釋執行。CPU最核心的功能單元包括:
寄存器(Register),是CPU內部的高速存儲器,像內存一樣可以存取數據,但比訪問內存快得多。隨後的幾章我們會詳細介紹x86的寄存器eax
、esp
、eip
等等,有些寄存器只能用於某種特定的用途,比如eip
用作程序計數器,這稱為特殊寄存器(Special-purpose Register),而另外一些寄存器可以用在各種運算和讀寫內存的指令中,比如eax
寄存器,這稱為通用寄存器(General-purpose Register)。
程序計數器(PC,Program Counter),是一種特殊寄存器,保存着CPU取下一條指令的地址,CPU按程序計數器保存的地址去內存中取指令然後解釋執行,這時程序計數器保存的地址會自動加上該指令的長度,指向內存中的下一條指令。
指令譯碼器(Instruction Decoder)。CPU取上來的指令由若干個位元組組成,這些位元組中有些位表示內存地址,有些位表示寄存器編號,有些位表示這種指令做什麼操作,是加減乘除還是讀寫內存,指令譯碼器負責解釋這條指令的含義,然後調動相應的執行單元去執行它。
算術邏輯單元(ALU,Arithmetic and Logic Unit)。如果譯碼器將一條指令解釋為運算指令,就調動算術邏輯單元去做運算,比如加減乘除、位運算、邏輯運算。指令中會指示運算結果保存到哪裡,可能保存到寄存器中,也可能保存到內存中。
地址和數據匯流排(Bus)。CPU和內存之間用地址匯流排、數據匯流排和控制綫連接起來,每條線上有1和0兩種狀態。如果在執行指令過程中需要訪問內存,比如從內存讀一個數到寄存器,執行過程可以想像成這樣:
CPU內部將寄存器對接到數據匯流排上,使寄存器的每一位對接到一條數據綫,等待接收數據。
CPU通過控制綫發一個讀請求,並且將內存地址通過地址綫發給內存。
內存收到地址和讀請求之後,將相應的內存單元對接到數據匯流排的另一端,這樣,內存單元每一位的1或0狀態通過一條數據綫到達CPU寄存器中相應的位,就完成了數據傳送。
往內存裡寫數據的過程與此類似,只是數據線上的傳輸方向相反。
上圖中畫了32條地址綫和32條數據綫,CPU寄存器也是32位,可以說這種體繫結構是32位的,比如x86就是這樣的體繫結構,目前主流的處理器是32位或64位的。地址綫、數據綫和CPU寄存器的位數通常是一致的,從上圖可以看出數據綫和CPU寄存器的位數應該一致,另外有些寄存器(比如程序計數器)需要保存一個內存地址,因而地址綫和CPU寄存器的位數也應該一致。處理器的位數也稱為字長,字(Word)這個概念用得比較混亂,在有些上下文中指16位,在有些上下文中指32位(這種情況下16位被稱為半字Half Word),在有些上下文中指處理器的字長,如果處理器是32位那麼一個字就是32位,如果處理器是64位那麼一個字就是64位。32位計算機有32條地址綫,地址空間(Address Space)從0x00000000到0xffffffff,共4GB,而64位計算機有更大的地址空間。
最後還要說明一點,本節所說的地址綫、數據綫是指CPU的內匯流排,是直接和CPU的執行單元相連的,內匯流排經過MMU和匯流排介面的轉換之後引出到晶片引腳才是外匯流排,外地址綫和外數據綫的位數都有可能和內匯流排不同,例如32位處理器的外地址匯流排可定址的空間可以大於4GB,到第 4 節 “MMU”再詳細解釋。
我們結合表 1.1 “一個語句的三種表示”看一下CPU取指執行的過程。
eip
寄存器指向地址0x80483a2,CPU從這裡開始取一條5個位元組的指令,然後eip
寄存器指向下一條指令的起始地址0x80483a7。
CPU對這5個位元組譯碼,得知這條指令要求從地址0x804a01c開始取4個位元組保存到eax
寄存器。
執行指令,讀內存,取上來的數是3,保存到eax
寄存器。注意,地址0x804a01c~0x804a01f裡存儲的四個位元組不能按地址從低到高的順序看成0x03000000,而要按地址從高到低的順序看成0x00000003。也就是說,對於多位元組的整數類型,低地址保存的是整數的低位,這稱為小端(Little Endian)位元組序(Byte Order)。x86平台是小端位元組序的,而另外一些平台規定低地址保存整數的高位,稱為大端(Big Endian)位元組序。
CPU從eip
寄存器指向的地址取一條3個位元組的指令,然後eip
寄存器指向下一條指令的起始地址0x80483aa。
CPU對這3個位元組譯碼,得知這條指令要求把eax
寄存器的值加1,結果仍保存到eax
寄存器。
執行指令,現在eax
寄存器中的數是4。
CPU從eip
寄存器指向的地址取一條5個位元組的指令,然後eip
寄存器指向下一條指令的起始地址0x80483af。
CPU對這5個位元組譯碼,得知這條指令要求把eax
寄存器的值保存到從地址0x804a018開始的4個位元組。
執行指令,把4這個值保存到從地址0x804a018開始的4個位元組(按小端位元組序保存)。