由底層和邏輯說開去 記憶體對齊機制深入剖析

2021-06-19 09:35:56 字數 3033 閱讀 4499

在我的部落格      

裡面提到記憶體對齊機制,我覺得這個有點意思,但是卻不能僅從彙編層面就分析出個所以然來,因此就從inter的cpu 對記憶體訪問路線的角度分析分析。

對於記憶體對齊 我們可以提出的問題從邏輯層面講,就是記憶體對齊有什麼意義,從底層來講就是cpu怎麼訪問記憶體;

首先記憶體是線性的 (雖然畫的不好但是輪廓還在)這個大家都知道 沒什麼神奇之處,但是聰明的cpu能讓它程式設計從邏輯意思上講的神奇的東西,現在大多數cpu對記憶體的定址採用的是位元組定址,也就是乙個位址線連乙個位元組(byte=8bit),如上圖我們看到的,這個圖也就解釋了cpu內部暫存器+1 指向的位置就是下乙個位元組,而不是下乙個bit或者乙個字,(其實這個也是乙個技術,不然只能讀乙個bit就更慢了,讓8條資料線(cpu匯流排上有許多條databus資料線 )連乙個位元組,這樣一次就能讀乙個位元組了)然後呢,顯然這樣每次讀數的時候很不爽啦,每次唯讀乙個位元組,也要讀到死啊,cpu自然不想被慢死,所以就採用了另乙個技術,叫作給記憶體分體,這個分體技術不想乙個位元組8個位這樣有明確要求的,不同位數的cpu分體的多少不一樣,怎麼理解這個分體技術呢,我們先從邏輯上理解一下,就想我們住的宿舍樓,每個位元組就像乙個宿舍,導員要想來查宿舍把每個學生抓起來,就要從服務台那裡拿到鑰匙和8條繩子,拿到這把鑰匙就能訪問到我們這個宿舍裡面的8個人了,每個人相當於記憶體裡一位,用一條繩子拴住,8個人就是乙個位元組共享這麼乙個位址(感覺說成監獄好一點);那麼分體是什麼呢,導員想了,哎呀我想訪問一樓的學生,總不能讓我每次抓8個出來再進去抓8個吧,於是就拿了更多的繩子更多的鑰匙,把一群人一次性的抓走了;嗯這一群人就是乙個體;

cpu也是這樣子,這樣一次訪問就可以訪問多個資料了,比如32位cpu一次總是能夠找到四個位元組(一定是四個,4個位元組就是32bit),(分體對於32位cpu就是把32個bit分成4個體,每個體管乙個位元組)但是又有問題了,有些位元組是不用訪問的啊,比如說乙個char 你總不能一下子給我讀四個位元組吧,是的32位cpu有32條資料線 每8條指向乙個位元組 所以可以指向四個位元組 可以通過控制電路,讓要讀的那個位元組可以導通,其它線路不通,這樣雖然32條資料線都有連線,只有乙個位元組能夠讀到資料,嗯也就是說32位cpu對記憶體的訪問是分了4個體,(這四個體構成乙個訪問單位)就像10個宿舍構成一層樓,cpu可以訪問這四個體中的乙個(char)兩個(short)三個(3個char不管是否連著)或四個(int);嗯這是一次讀取的,而對於double這種就需要兩次了,嗯 所以問題就來了,哎呀我的double讀的時間是你int的兩倍,不能忍啊,於是就出現了奔騰時代的64位,現在的個人cpu大多數是64位,我們就只討論這些主流的cpu ,是的 64位分8個體 所以double也只用一次就讀到了,好快噠~~

這樣我們就用上述的原理來解釋一下c語言裡的結論,來佐證我們的原理是正確的有用的;

win32平台下的微軟c編譯器(cl.exefor 80×86

)的對齊策略:

1)結構體變數的首位址是其最長基本型別成員的整數倍;

2)結構體每個成員相對於結構體首位址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組(internal

adding);

備註:為結構體的乙個成員開闢空間之前,編譯器首先檢查預開闢空間的首位址相對於結構體首位址的偏移是否是本成員的整數倍,若是,則存放本成員,反之,則在本成員和上乙個成員之間填充一定的位元組,以達到整數倍的要求,也就是將預開闢空間的首位址後移幾個位元組。

3)結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要,編譯器會在最末乙個成員之後加上填充位元組(trailing padding)。

備註:結構體總大小是包括填充位元組,最後乙個成員滿足上面兩條以外,還必須滿足第三條,否則就必須在最後填充幾個位元組以達到本條要求。

4) 結構體內型別相同的連續元素將在連續的空間內,和陣列一樣。

5) 如果結構體內存在長度大於處理器位數的元素,那麼就以處理器的倍數為對齊單位;否則,如果結構體內的元素的長度都小於處理器的倍數的時候,便以結構體裡面最長的資料元素為對齊單位。

1

)結構體變數的首位址是其最長基本型別成員的整數倍;這個條款要和2)結構體每個成員相對於結構體首位址的偏移量(offset)都是成員大小的整數倍,一起理解;

嗯  我們來看乙個結構:

struct

text;

分類討論一下 :這個結構的最大元素是double  8個位元組 自然 結構的首位址要在8的整數倍處(因為每行的開頭就是8的倍數(行是從邏輯上言的方便理解 其實記憶體還是線性的)), 也就是說a要在1處 (a1 b1 c1或d1)    為什麼首位址要是double的倍數呢,因為這樣這個d就能夠獨佔一行了並且達到整個結構體所佔行數最小了(為什麼要獨佔一行:一次性讀完)(為什麼結構要占行數少:讀的次數少)

3)結構體的總大小為結構體最寬基本型別成員大小的整數倍:這個肯定是啊,a佔乙個位元組,後面的b 與a之間填充乙個位元組  然後b佔(這個位置是16位cpu的乙個體) 所以c之前已經佔了四個 c再佔四個就是8個 然後d佔8個 就是16個,這個結構可能偶然 可以再試試別的;

struct

text;

16個位元組; 為什麼是16個不是9個 兩次cpu讀取都一樣  不要怪cpu浪費 這只是它的生活態度~~~

4) 結構體內型別相同的連續元素將在連續的空間內,和陣列一樣。

這句話沒什麼說的;

5) 如果結構體內存在長度大於處理器位數的元素,那麼就以處理器的倍數為對齊單位;否則,如果結構體內的元素的長度都小於處理器的倍數的時候,便以結構體裡面最長的資料元素為對齊單位。

這個意思就是說 如果有個資料型別超過8個位元組(long double) 那麼就要cpu讀兩次才能讀完了(邏輯上講一行存不下)  所以就按照cpu的位數64bit 是8位元組,進行對齊;

這樣 這個話題就說完了,不過我這裡說的分體技術 只適合 8位cpu(8086機)以後的(因為8bit就是1個位元組 怎麼分體)  比如16位機(80286)32位機(80386與80486)以及後來的奔騰機和酷睿機等等等等; 

由底層和邏輯說開去 記憶體對齊機制深入剖析

裡面提到記憶體對齊機制,我覺得這個有點意思,但是卻不能僅從彙編層面就分析出個所以然來,因此就從inter的cpu 對記憶體訪問路線的角度分析分析。對於記憶體對齊 我們可以提出的問題從邏輯層面講,就是記憶體對齊有什麼意義,從底層來講就是cpu怎麼訪問記憶體 首先記憶體是線性的 雖然畫的不好但是輪廓還在...

由自由說開去

今天又遲到了,居然又是1分鐘,的確是令人居喪的一件事,而且坐在位置上後眼睛看東西突然就模糊起來了,還在持續中.這一繼續就又是乙個禮拜過去了。本來想在假期裡寫寫的,可是就那麼過去了,總覺得日子過的飛快,而自己又成長的太慢。時間總在自己的期待中流逝,而自己總在時間流逝後期待,就這麼反覆著,但光陰卻沒有為...

由socket的accept說開去

討論完後,才發現,自己雖然熟悉socket的程式設計套路,但是卻並不是那麼清楚socket的原理,今天就趁這個機會,把有關socket程式設計的幾個疑問給搞清楚吧。先給出乙個典型的tcp ip通訊示意圖。問題一 socket結構體物件究竟是怎樣定義的?我們知道,在使用socket程式設計之前,需要呼...