處理器如何訪問(wèn)內(nèi)存?了解有關(guān)C語(yǔ)言結(jié)構(gòu)以及如何使用它們的更多信息。
本文將首先解釋內(nèi)存訪問(wèn)粒度的概念,以便我們可以對(duì)處理器如何訪問(wèn)內(nèi)存有一個(gè)基本的了解。然后,我們將仔細(xì)研究數(shù)據(jù)對(duì)齊的概念,并研究一些示例結(jié)構(gòu)的內(nèi)存布局。
在上一篇有關(guān)嵌入式C中的結(jié)構(gòu)的文章中,我們觀察到重新排列結(jié)構(gòu)中成員的順序可以更改存儲(chǔ)結(jié)構(gòu)所需的內(nèi)存量。我們還看到,當(dāng)為結(jié)構(gòu)的成員分配內(nèi)存時(shí),編譯器具有某些約束。這些被稱為數(shù)據(jù)對(duì)齊要求的約束條件允許處理器以可能在內(nèi)存布局中出現(xiàn)的一些浪費(fèi)空間(稱為“填充”)為代價(jià)更有效地訪問(wèn)變量。
本文將首先解釋內(nèi)存訪問(wèn)粒度的概念,以便我們可以對(duì)處理器如何訪問(wèn)內(nèi)存有一個(gè)基本的了解。然后,我們將仔細(xì)研究數(shù)據(jù)對(duì)齊的概念,并研究一些示例結(jié)構(gòu)的內(nèi)存布局。
值得一提的是,計(jì)算機(jī)的存儲(chǔ)系統(tǒng)可能比這里介紹的復(fù)雜得多。本文的目的是討論一些對(duì)嵌入式系統(tǒng)進(jìn)行編程時(shí)可能有用的基本概念。
內(nèi)存訪問(wèn)粒度
我們通常將內(nèi)存設(shè)想為單字節(jié)存儲(chǔ)位置的集合,如圖1所示。這些位置中的每一個(gè)都有一個(gè)唯一的地址,該地址使我們可以訪問(wèn)該地址的數(shù)據(jù)。
圖1
但是,處理器通常以大于一個(gè)字節(jié)的塊的形式訪問(wèn)內(nèi)存。例如,處理器可以按四個(gè)字節(jié)的塊訪問(wèn)內(nèi)存。在這種情況下,我們可以設(shè)想圖1的12個(gè)連續(xù)字節(jié),如下圖2所示。
圖2
您可能想知道這兩種處理內(nèi)存的方式有何區(qū)別。在圖1中,處理器一次讀取一個(gè)字節(jié)并將其寫(xiě)入內(nèi)存。請(qǐng)注意,在讀取或?qū)懭?/span>存儲(chǔ)器位置之前,我們需要訪問(wèn)該存儲(chǔ)器單元,并且每次訪問(wèn)存儲(chǔ)器都需要一段時(shí)間。假設(shè)我們要讀取圖1中的內(nèi)存的前八個(gè)字節(jié)。對(duì)于每個(gè)字節(jié),處理器都需要訪問(wèn)內(nèi)存并讀取它。因此,要讀取前八個(gè)字節(jié)的內(nèi)容,處理器將必須訪問(wèn)存儲(chǔ)器八次。
使用圖2,處理器一次從存儲(chǔ)器中讀取和寫(xiě)入四個(gè)字節(jié)。因此,為了讀取前四個(gè)字節(jié),處理器訪問(wèn)存儲(chǔ)器的地址0,并讀取四個(gè)連續(xù)的存儲(chǔ)位置(地址0至3)。同樣,要讀取下一個(gè)四個(gè)字節(jié)的塊,處理器需要再訪問(wèn)一次內(nèi)存。它轉(zhuǎn)到地址4,并同時(shí)從地址4到7讀取存儲(chǔ)位置。對(duì)于字節(jié)大小的塊,需要八個(gè)內(nèi)存訪問(wèn)才能讀取八個(gè)連續(xù)的內(nèi)存字節(jié)。但是,對(duì)于圖2,僅需要兩次內(nèi)存訪問(wèn)。如上所述,每次內(nèi)存訪問(wèn)都需要一些時(shí)間。由于圖2所示的內(nèi)存配置減少了訪問(wèn)次數(shù),因此可以提高處理效率。
處理器訪問(wèn)內(nèi)存時(shí)使用的數(shù)據(jù)大小稱為內(nèi)存訪問(wèn)粒度。圖2描述了具有四字節(jié)內(nèi)存訪問(wèn)粒度的系統(tǒng)。
內(nèi)存訪問(wèn)邊界
硬件設(shè)計(jì)人員經(jīng)常采用另一項(xiàng)重要技術(shù)來(lái)使處理系統(tǒng)更高效:他們限制處理器,使其只能在特定邊界訪問(wèn)內(nèi)存。例如,處理器可能只能在四字節(jié)邊界訪問(wèn)圖2的內(nèi)存,如圖3中的紅色箭頭所示。
圖3
這種邊界限制會(huì)大大提高系統(tǒng)效率嗎?讓我們仔細(xì)看看。假定我們需要讀取地址為3和4(由圖3中的綠色和藍(lán)色矩形表示)的存儲(chǔ)單元的內(nèi)容。如果處理器可以從任意地址開(kāi)始讀取四字節(jié)的塊,我們可以訪問(wèn)地址3并通過(guò)一次內(nèi)存訪問(wèn)來(lái)讀取兩個(gè)所需的內(nèi)存位置。但是,如上所述,處理器不能直接訪問(wèn)任意地址。相反,它僅在特定邊界訪問(wèn)存儲(chǔ)器。那么,如果處理器只能訪問(wèn)四個(gè)字節(jié)的邊界,那么它將如何讀取地址3和4的內(nèi)容呢?
由于內(nèi)存訪問(wèn)邊界的限制,處理器必須訪問(wèn)地址為0的內(nèi)存位置并讀取四個(gè)連續(xù)的字節(jié)(地址0至3)。接下來(lái),它必須使用移位操作將地址3的內(nèi)容與其他三個(gè)字節(jié)(地址0到2)分開(kāi)。類似地,處理器可以訪問(wèn)地址4并從地址4到7讀取另一個(gè)四字節(jié)的塊。最后,可以使用移位操作將所需的字節(jié)(藍(lán)色矩形)與其他三個(gè)字節(jié)分開(kāi)。
如果沒(méi)有內(nèi)存訪問(wèn)邊界限制,我們可以通過(guò)一次內(nèi)存訪問(wèn)讀取地址3和4。但是,邊界限制迫使處理器兩次訪問(wèn)內(nèi)存。那么,如果它使數(shù)據(jù)操作更加困難,為什么還要將內(nèi)存訪問(wèn)限制在某些邊界呢?存在內(nèi)存訪問(wèn)邊界限制是因?yàn)閷?duì)地址進(jìn)行某些假設(shè)可以簡(jiǎn)化硬件設(shè)計(jì)。例如,假設(shè)需要32位來(lái)尋址內(nèi)存塊中的所有字節(jié)。如果我們將地址限制為四字節(jié)邊界,則32位地址的兩個(gè)最低有效位將始終為零(因?yàn)樵摰刂穼⑹冀K被4整除)。因此,我們將能夠使用30位來(lái)尋址2 32字節(jié)的內(nèi)存。
數(shù)據(jù)對(duì)齊
既然我們知道基本處理器如何訪問(wèn)內(nèi)存,我們就可以討論數(shù)據(jù)對(duì)齊要求。通常,任何K字節(jié)的C數(shù)據(jù)類型都必須具有K的倍數(shù)的地址。例如,四字節(jié)的數(shù)據(jù)類型只能存儲(chǔ)在地址0、4、8,…;不能將其存儲(chǔ)在地址1、2、3、5,...。這樣的限制簡(jiǎn)化了處理器和存儲(chǔ)系統(tǒng)之間接口硬件的設(shè)計(jì)。
例如,考慮具有四字節(jié)內(nèi)存訪問(wèn)粒度的處理器,該處理器只能在四字節(jié)邊界訪問(wèn)內(nèi)存。假設(shè)有一個(gè)四字節(jié)變量存儲(chǔ)在地址1中,如圖4所示(這四個(gè)字節(jié)對(duì)應(yīng)于四種不同的顏色)。在這種情況下,我們將需要兩次內(nèi)存訪問(wèn)和一些額外的工作才能讀取未對(duì)齊的四字節(jié)數(shù)據(jù)(“未對(duì)齊”是指將其拆分為兩個(gè)四個(gè)字節(jié)的塊)。該過(guò)程如圖所示。
圖4
但是,如果我們將四字節(jié)變量存儲(chǔ)在4的倍數(shù)的任何地址上,則只需要一次內(nèi)存訪問(wèn)即可修改數(shù)據(jù)或讀取數(shù)據(jù)。
這就是為什么將K字節(jié)數(shù)據(jù)類型存儲(chǔ)在K的倍數(shù)的地址可以使系統(tǒng)效率更高的原因。因此,可以將C語(yǔ)言“char”變量(僅需要一個(gè)字節(jié))存儲(chǔ)在任何字節(jié)地址,但是必須將兩個(gè)字節(jié)的變量存儲(chǔ)在偶數(shù)地址。四字節(jié)類型必須從可被4整除的地址開(kāi)始,八字節(jié)數(shù)據(jù)類型必須被存儲(chǔ)在被8整除的地址中。例如,假設(shè)在特定計(jì)算機(jī)上,“short”變量需要兩個(gè)字節(jié),“int”和“float”類型占用四個(gè)字節(jié),而“long”,“double””,而指針占據(jù)八個(gè)字節(jié)。這些數(shù)據(jù)類型中的每一個(gè)通常都應(yīng)具有K的倍數(shù)的地址,其中K由下表給出。
請(qǐng)注意,不同數(shù)據(jù)類型的大小可能會(huì)因編譯器和計(jì)算機(jī)體系結(jié)構(gòu)而異。sizeof()運(yùn)算符將是查找數(shù)據(jù)類型的實(shí)際大小的最佳方法。
結(jié)構(gòu)的內(nèi)存布局
現(xiàn)在,讓我們檢查一下結(jié)構(gòu)的內(nèi)存布局。考慮為32位計(jì)算機(jī)編譯以下結(jié)構(gòu):
structTest2{ uint8_tc; uint32_td; uint8_te; uint16_tf; }MyStruct;
我們知道將分配四個(gè)內(nèi)存位置以在結(jié)構(gòu)中存儲(chǔ)成員,并且內(nèi)存位置的順序?qū)⑴c聲明成員的順序匹配。第一個(gè)成員是一個(gè)字節(jié)的變量,可以存儲(chǔ)在任何地址。因此,第一個(gè)可用的存儲(chǔ)位置將分配給該變量。假定如圖5所示,編譯器將地址0分配給該變量。下一個(gè)成員是四字節(jié)數(shù)據(jù)類型,只能存儲(chǔ)在4的倍數(shù)的地址上。第一個(gè)可用的存儲(chǔ)位置是地址4。但是,這需要保留地址1、2和3未被使用。如您所見(jiàn),數(shù)據(jù)對(duì)齊要求導(dǎo)致了內(nèi)存布局中一些浪費(fèi)的空間(或填充)。
下一個(gè)成員是e,它是一個(gè)一字節(jié)的變量。可以將第一個(gè)可用的存儲(chǔ)位置(圖5中的地址8)分配給該變量。接下來(lái),我們到達(dá)f,它是一個(gè)兩個(gè)字節(jié)的變量。可以將其存儲(chǔ)在可被2整除的地址中。第一個(gè)可用空間是地址10。如您所見(jiàn),將出現(xiàn)更多的填充以滿足數(shù)據(jù)對(duì)齊要求。
圖5
我們希望該結(jié)構(gòu)占用8個(gè)字節(jié),但實(shí)際上需要12個(gè)字節(jié)。有趣的是,如果我們了解數(shù)據(jù)對(duì)齊要求,則可以重新排列結(jié)構(gòu)中成員的順序,并提高內(nèi)存使用效率。例如,讓我們重寫(xiě)下面的結(jié)構(gòu),其中成員從最大到最小排列。
structTest2{ uint32_td; uint16_tf; uint8_tc; uint8_te; }MyStruct;
在32位計(jì)算機(jī)上,上述結(jié)構(gòu)的內(nèi)存布局可能類似于圖6中所示的布局。
圖6
第一個(gè)結(jié)構(gòu)需要12個(gè)字節(jié),而新結(jié)構(gòu)僅需要8個(gè)字節(jié)。這是一項(xiàng)重大改進(jìn),尤其是在內(nèi)存受限的嵌入式處理器的情況下。
另外,請(qǐng)注意,結(jié)構(gòu)的最后一個(gè)成員之后可能會(huì)有一些填充字節(jié)。結(jié)構(gòu)的總大小必須被其最大成員的大小整除。考慮以下結(jié)構(gòu):
structTest3{ uint32_tc; uint8_td; }MyStruct2;
在這種情況下,內(nèi)存布局將如圖7所示。您可以看到,在內(nèi)存布局的末尾添加了三個(gè)填充字節(jié),以將結(jié)構(gòu)的大小增加到8個(gè)字節(jié)。這將使結(jié)構(gòu)大小可被結(jié)構(gòu)中較大成員(c成員,這是一個(gè)四字節(jié)的變量)的大小整除。
圖7
概要
-
處理器通常以大于一個(gè)字節(jié)的塊的形式訪問(wèn)內(nèi)存。這樣可以提高系統(tǒng)效率。
-
處理器訪問(wèn)內(nèi)存時(shí)使用的數(shù)據(jù)大小是處理器的內(nèi)存訪問(wèn)粒度。
-
處理器可能被限制為僅在某些邊界(例如,在四字節(jié)邊界)訪問(wèn)存儲(chǔ)器。
-
存在此內(nèi)存訪問(wèn)限制是因?yàn)閷?duì)地址進(jìn)行某些假設(shè)可以簡(jiǎn)化硬件設(shè)計(jì)。
-
通常,任何K字節(jié)的C數(shù)據(jù)類型都必須具有K的倍數(shù)的地址。此類限制簡(jiǎn)化了處理器與內(nèi)存系統(tǒng)之間接口硬件的設(shè)計(jì)。
-
數(shù)據(jù)對(duì)齊要求導(dǎo)致內(nèi)存布局中的某些空間浪費(fèi)(或填充)。
-
結(jié)構(gòu)的最后一個(gè)成員之后可能會(huì)有一些填充字節(jié)。結(jié)構(gòu)的總大小必須被其最大成員的大小整除。
-
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136841 -
結(jié)構(gòu)體
+關(guān)注
關(guān)注
1文章
130瀏覽量
10844
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論