虛擬地址
所謂虛擬地址空間,就是程序可以使用的虛擬地址的有效范圍。虛擬地址和物理地址的映射關系由操作系統決定,相應地,虛擬地址空間的大小也由操作系統決定,但還會受到編譯模式的影響。這節我們先講解CPU,再講解編譯模式,讓大家了解編譯器是如何配合CPU來提高程序運行速度的。
CPU的數據處理能力
CPU是計算機的核心,決定了計算機的數據處理能力和尋址能力,也即決定了計算機的性能。CPU一次(一個時鍾內)能處理的數據的大小由寄存器的位數和數據總線的寬度(也即有多少根數據總線)決定,我們通常所說的多少位的CPU,除了可以理解為寄存器的位數,也可以理解數據總線的寬度,通常情況下它們是相等的。
數據總線位於主板之上,不在CPU中,也不由CPU決定,嚴格來講,這里應該說CPU能夠支持的數據總線的最大根數,也即能夠支持的最大數據處理能力,為了表達方便,本文才使用“CPU的數據總線”這一說法。
數據總線和主頻都是CPU的重要指標:數據總線決定了CPU單次的數據處理能力,主頻決定了CPU單位時間內的數據處理次數,它們的乘積就是CPU單位時間內的數據處理量。
我們常常聽說,CPU主頻在計算機的發展過程中飛速提升,從最初的幾十 KHz,到后來的幾百 MHz,再到現在的 4GHz,終於因為硅晶體的物理特性很難再提升,只能向多核方向發展。在這個過程中,CPU的數據總線寬度也在成倍增長,從早期的8位、16位,到后來的32位,現在我們計算機大部分都在使用64位CPU。
需要注意的是,數據總線和地址總線不是一回事,數據總線用於在CPU和內存之間傳輸數據,地址總線用於在內存上定位數據,它們之間沒有必然的聯系,寬度並不一定相等。實際情況是,地址總線的寬度往往隨着數據總線的寬度而增長,以訪問更大的內存。
- 16位CPU
早期的CPU是16位的,一次能處理 16Bit(2個字節)的數據。這個時候計算機產業還處在早期,個人電腦也沒有進入千家萬戶,也沒有提出虛擬地址的概念,程序還是直接運行在物理內存上,操作系統對內存的管理非常簡陋,程序員輕易就能編寫一個惡意程序去修改其他程序的內存。
學過匯編的同學應該知道,典型的16位處理器是 Intel 8086,它的數據總線有16根,地址總線有20根,尋址能力為 2^20 = 1MB。
2) 32位CPU
隨着計算機產業的進步,出現了32位的CPU,一次能處理 32Bit(4個字節)的數據。這個時候就提出了虛擬地址的概念,並被應用到CPU和操作系統中,由它們共同完成虛擬地址和物理地址的映射,這使得程序編寫更加容易,運行更加安全。
典型的32位處理器是 Intel 的 80386 和 Intel Pentium 4(奔騰4):80386 的數據總線和地址總線寬度都是32位,尋址能力達4GB;Pentium 4的地址總線寬度是36位,理論尋址能力達64GB。
3) 64位CPU
現代計算機都使用64位的CPU,它們一次能處理64Bit(8個字節)的數據。典型的64位處理器是 Intel 的 Core i3、i5、i7 等,它們的地址總線寬度為 40~50 位左右。64位CPU的出現使個人電腦再次發生了質的飛躍。
實際支持的物理內存
CPU支持的物理內存只是理論上的數據,實際應用中還會受到操作系統的限制,例如,Win7 64位家庭版最大僅支持8GB或16GB的物理內存,Win7 64位專業版或企業版能夠支持到192GB的物理內存。
Windows Server 2003 數據中心版專為大型企業或國家機構而設計,可以處理海量數據,分為32位版和64位版,32位版最高支持512GB的物理內存,這顯然超出了32位CPU的尋址能力,可以通過兩次尋址來實現。
編譯模式
為了兼容不同的平台,現代編譯器大都提供兩種編譯模式:32位模式和64位模式。
32位編譯模式
在32位模式下,一個指針或地址占用4個字節的內存,共有32位,理論上能夠訪問的虛擬內存空間大小為 2^32 = 0X100000000 Bytes,即4GB,有效虛擬地址范圍是 0 ~ 0XFFFFFFFF。
也就是說,對於32位的編譯模式,不管實際物理內存有多大,程序能夠訪問的有效虛擬地址空間的范圍就是0 ~ 0XFFFFFFFF,也即虛擬地址空間的大小是 4GB。換句話說,程序能夠使用的最大內存為 4GB,跟物理內存沒有關系。
如果程序需要的內存大於物理內存,或者內存中剩余的空間不足以容納當前程序,那么操作系統會將內存中暫時用不到的一部分數據寫入到磁盤,等需要的時候再讀取回來,這在《載入內存,讓程序運行起來》中已經講到。而我們的程序只管使用 4GB 的內存,不用關心硬件資源夠不夠。
如果物理內存大於 4GB,例如目前很多PC機都配備了8GB的內存,那么程序也無能為力,它只能夠使用其中的 4GB。
64位編譯模式
在64位編譯模式下,一個指針或地址占用8個字節的內存,共有64位,理論上能夠訪問的虛擬內存空間大小為 2^64。這是一個很大的值,幾乎是無限的,就目前的技術來講,不但物理內存不可能達到這么大,CPU的尋址能力也沒有這么大,實現64位長的虛擬地址只會增加系統的復雜度和地址轉換的成本,帶不來任何好處,所以 Windows 和 Linux 都對虛擬地址進行了限制,僅使用虛擬地址的低48位(6個字節),總的虛擬地址空間大小為 2^48 = 256TB。
需要注意的是:
32位的操作系統只能運行32位的程序(也即以32位模式編譯的程序),64位操作系統可以同時運行32位的程序(為了向前兼容,保留已有的大量的32位應用程序)和64位的程序(也即以64位模式編譯的程序)。
64位的CPU運行64位的程序才能發揮它的最大性能,運行32位的程序會白白浪費一部分資源。
目前計算機可以說已經進入了64位的時代,之所以還要提供32位編譯模式,是為了兼容一些老的硬件平台和操作系統,或者某些場合下32位的環境已經足夠,使用64位環境會增大成本,例如嵌入式系統、單片機、工控等。
這里所說的32位環境是指:32位的CPU + 32位的操作系統 + 32位的程序。
另外需要說明的是,32位環境擁有非常經典的設計,易於理解,適合教學,現有的很多資料都是以32位環境為基礎進行講解的。本教程也是如此,除非特別指明,否則都是針對32位環境。相比於32位環境,64位環境的設計思路並沒有發生質的變化,理解了32環境很容易向64位環境遷移。
開啟64位編譯模式
以 VS2010 為例,創建工程后默認是32位的,如下圖所示:
“Win32”表示32位編譯模式。如果要以64位的方式編譯,就需要新增編譯模式,如下圖所示:
選擇“配置管理器”,彈出如下的對話框:
在“活動解決方案平台”下選擇“新建”,彈出下面的對話框:
在下拉菜單中選擇“x64”,即可新增64位編譯模式。現在,我們就可以在兩種編譯模式之間進行切換了:
將下面的代碼復制到源文件中:
#include <stdio.h>
#include <stdlib.h>
int a;
int main(){
int *p = &a;
printf("%#X, %d\n", p, sizeof(int*));
system("pause");
return 0;
}
在 Win32 編譯模式下的結果:
0XB715C, 4
在 x64 編譯模式下的結果:
0X3FF39740, 8