內(nèi)存的編址與尋址
首先什么是內(nèi)存?要理解內(nèi)存首先要了解內(nèi)存有兩個視圖,一個是邏輯視圖,一個是物理視圖,對應地址空間就是我們常說的邏輯地址空間和物理地址空間,如果內(nèi)存在管理上采用的是虛擬內(nèi)存擴展技術,那這時候把邏輯地址空間也叫虛擬地址空間,兩者還是有差別的(差別不屬于本文要說的內(nèi)容,感興趣的可自行查看如x86實模式的邏輯地址和物理地址間的關系,實模式就沒用虛擬內(nèi)存機制,那個年代沒出這種高科技),很多地方把兩者等同了,主要是現(xiàn)在的操作系統(tǒng)大都采用的是虛擬內(nèi)存擴展技術。
在采用虛擬內(nèi)存管理機制的操作系統(tǒng)控制下的計算機中,CPU地址線出來的地址都是虛擬地址,操作系統(tǒng)要經(jīng)過MMU(Memory Manage Unit)內(nèi)存管理單元對虛擬地址進行轉換,才能得到真實的物理地址(連接到物理內(nèi)存上的),站在CPU指令級層面,計算的操作數(shù)或指令的內(nèi)存有效地址EA此時都是虛擬地址,這個地址空間就是下面我們要討論的程序視角內(nèi)存。

1、??? 什么是內(nèi)存:
這里說的內(nèi)存是從程序角度看,內(nèi)存是按地址訪問的一維線性空間。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

這個一維存儲空間是對物理內(nèi)存的抽象描述,怎么理解這里的抽象呢,就是從程序角度屏蔽了物理內(nèi)存不同的存儲字寬、地址線信息、甚至存取周期等差異,在程序端,就是按地址訪問存儲單元。
內(nèi)存由大量的存儲單元組成,每個存儲單元有一個唯一的編號,這個編號就是我們說的該存儲單元的地址,用該地址可以唯一地訪問到一個存儲單元(重要的事情來回多說幾遍)。
每個存儲單元可以存放多個二進制位(bit),一般是字節(jié)(Byte)的整數(shù)倍,在計算機里定義1字節(jié) = 8位。
接下來就是按多大的存儲單元來設置地址了,引出了第二個概念----編址方式。
2、??? 什么是編址方式?
編址方式就是按照多大的存儲單元分割內(nèi)存。目前常用的編址方式有字節(jié)編址和字編址,大多數(shù)計算機采用的是字節(jié)編址。
字節(jié)編址:一個內(nèi)存地址對應一個字節(jié)信息;
字編址(字指機器字長):一個內(nèi)存地址對應一個字信息;
還是用例子說名下:假設一個字=32bits(位) =4Byte(字節(jié))
下圖是兩者的對照圖示:

為了讓大家理解兩者的區(qū)別,我們再看下在同樣的存儲容量下,不同的編址方式對物理地址尋址范圍的要求和相應地址線的影響。

地址分配問題解決了,接下來我們討論多字節(jié)數(shù)據(jù)在內(nèi)存中怎么存放的問題,從而引出了字節(jié)順序的概念。
3、??? 什么是字節(jié)順序?
在字節(jié)編址的大背景下,一個多字節(jié)的數(shù)據(jù)在內(nèi)存中怎么存放呢?目前常用的有兩種存放方式:
大端(Big endian):低有效字節(jié)存放在高地址;
小端(Little endian):低有效字節(jié)存放在低地址。
通過例子看區(qū)別吧,如一個4字節(jié)數(shù)據(jù):01234567H(H代表十六進制數(shù),也可表示為0x01234567)

如果是在一些調(diào)試環(huán)境里看就是下圖的情況:

小端字節(jié)順序看到的和數(shù)據(jù)本身順序是反的,這是為什么后來的精簡指令集(RISC)很多都用大端字節(jié)順序的原因,大端字節(jié)順序更符合人類閱讀習慣,但是采用小端字節(jié)順序更利于硬件處理,不同長度數(shù)據(jù)的低位位置(地址)是相同的,在進行類型擴展時方便處理。采用小端的有x86處理器,采用大端字節(jié)順序的有MIPS處理器,ARM處理器是可配置的,也叫雙端口字節(jié)順序。以整數(shù)7為例,它以1個字節(jié)、2個字節(jié)、4個字節(jié)在小端和大端存儲時的內(nèi)存圖示:

由于存在兩種不同字節(jié)順序,不同處理器上使用的數(shù)據(jù)就不能拿來就用了,機器指令級程序更不存在拿來主義了(0/1指令編碼格式各不相同)。
內(nèi)存的編址和字節(jié)存放順序確定下來以后,還有另外一個問題,就是對齊方式:
1、??? 什么是對齊方式?也叫邊界對齊、字節(jié)對齊。
其定義:邊界對齊是信息寬度不超過主存寬度的信息必須存放在一個存儲字內(nèi),不能跨邊界。(說了好像跟沒說一樣)
其實就是:
字節(jié)信息的起始地址為:×…××××(x均為二進制,任意位置)
半字信息的起始地址為:×…×××0(2B對齊)
單字信息的起始地址為:×…××00(4B對齊)
雙字信息的起始地址為:×…×000(8B對齊)???
還是用圖說話吧:

為什么會采用邊界對齊呢?看上圖,其實就是計算機里面“用空間換時間”的策略實例,尤其在現(xiàn)在存儲空間綽綽有余的環(huán)境下,編譯器默認都是采用邊界對齊的(及右邊方式),不同的系統(tǒng)對不同類型的數(shù)據(jù)采用默認對齊方式還不一樣,例如C語言中的double類型在Linux系統(tǒng)下是4B對齊,在windows下是8B對齊。那為什么采用邊界對齊后就能更快獲取到數(shù)據(jù)?如上圖所示,對于雙字類型(圖中黃色背景區(qū)),如果采用非對齊方式(左圖),空間是節(jié)省了,但是訪存至少要兩次,之后還要對數(shù)據(jù)進行拼接,如果采用對齊方式(右圖),雖然浪費了空間,但是一個訪存周期就可以得到該雙字數(shù)據(jù),時間節(jié)省下來了。
知道了編制方式、字節(jié)順序和邊界對齊,接下來就是尋址了,尋址和上面幾點均相關。
5、??? 什么是尋址?
這里說的尋址不是指令集設計里的什么立即數(shù)尋址、寄存器尋址、直接尋址、間接尋址等等,這些尋址方式是解釋怎么由指令封裝中的形式地址得到內(nèi)存有效地址(EA)的方法。而本文說的尋址是強調(diào)通過給定地址怎么得到相應數(shù)據(jù),,更準確說是信息,因為程序和數(shù)據(jù)在內(nèi)存中是沒區(qū)別的,都是0、1信息。
還是用例子說話吧,如下圖:

通過這個例子,能看到,信息獲取和字的大小、編址方式、字節(jié)順序、對齊方式都相關。
如果是4B數(shù)據(jù),又要求邊界對齊(假設align是4),那么該數(shù)據(jù)在圖示的幾個地址中只能從0x0C開始存放。
了解上面之后,體會C語言中指針變量的類型的實際意義。
嘗試運行下面的C語言程序,并能解釋原因:
----------------------------------
#include <stdio.h>
char buff[]={3,2,1,0,131};
int a, b;
int main(){
??? char *p = buff;
??? a = *(int *)p;
??? b = *(int *)(p+1);
??? printf("a = 0x%x, b = 0x%x\n", a, b);
??? printf("a = %d, b = %d\n", a, b);
??? return 0;
}?
----------------------------------------

以上都是站在程序視角看的內(nèi)存空間,另外一個視角就是從物理內(nèi)存角度看內(nèi)存,這里包括內(nèi)存的位擴展、字擴展和并行存儲等概念,這些是計組中存儲器設計部分相關的內(nèi)容,不屬于本文設計內(nèi)容,感興趣可查閱相應資料。
?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?