虛擬地址空間以及編譯模式
所謂虛擬地址空間,就是程式可以使用的虛擬地址的有效範圍。虛擬地址和實體地址的對映關係由作業系統決定,相應地,虛擬地址空間的大小也由作業系統決定,但還會受到編譯模式的影響。
這節我們先講解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和記憶體之間傳輸資料,地址匯流排用於在記憶體上定位資料,它們之間沒有必然的聯絡,寬度並不一定相等。實際情況是,地址匯流排的寬度往往隨著資料匯流排的寬度而增長,以訪問更大的記憶體。
早期的CPU是16位的,一次能處理 16Bit(2個位元組)的資料。這個時候計算機產業還處在早期,個人電腦也沒有進入千家萬戶,也沒有提出虛擬地址的概念,程式還是直接執行在實體記憶體上,作業系統對記憶體的管理非常簡陋,程式設計師輕易就能編寫一個惡意程式去修改其他程式的記憶體。
學過彙編的同學應該知道,典型的16位處理器是 Intel 8086,它的資料匯流排有16根,地址匯流排有20根,定址能力為 2^20 = 1MB。
隨著計算機產業的進步,出現了32位的CPU,一次能處理 32Bit(4個位元組)的資料。這個時候就提出了虛擬地址的概念,並被應用到CPU和作業系統中,由它們共同完成虛擬地址和實體地址的對映,這使得程式編寫更加容易,執行更加安全。
典型的32位處理器是 Intel 的 80386 和 Intel Pentium 4(奔騰4):80386 的資料匯流排和地址匯流排寬度都是32位,定址能力達4GB;Pentium 4的地址匯流排寬度是36位,理論定址能力達64GB。
現代計算機都使用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位模式下,一個指標或地址佔用4個位元組的記憶體,共有32位,理論上能夠訪問的虛擬記憶體空間大小為 2^32 = 0X100000000 Bytes,即4GB,有效虛擬地址範圍是 0 ~ 0XFFFFFFFF。
也就是說,對於32位的編譯模式,不管實際實體記憶體有多大,程式能夠訪問的有效虛擬地址空間的範圍就是0 ~ 0XFFFFFFFF,也即虛擬地址空間的大小是 4GB。換句話說,程式能夠使用的最大記憶體為 4GB,跟實體記憶體沒有關係。
如果程式需要的記憶體大於實體記憶體,或者記憶體中剩餘的空間不足以容納當前程式,那麼作業系統會將記憶體中暫時用不到的一部分資料寫入到磁碟,等需要的時候再讀取回來,這在《載入記憶體,讓程式執行起來》中已經講到。而我們的程式只管使用 4GB 的記憶體,不用關心硬體資源夠不夠。
如果實體記憶體大於 4GB,例如目前很多PC機都配備了8GB的記憶體,那麼程式也無能為力,它只能夠使用其中的 4GB。
在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
轉自:https://blog.csdn.net/qq_28018113/article/details/73438094