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