記憶體對於程式設計師而言可以說是非常重要,但很多人對其只知其名,不知道它的內部原理。
看一下它的物理結構,大家肯定見過。
記憶體的內部是由各種ic電路組成的,主要分為3種
隨機儲存器(ram):讀寫很快,斷電失去資料。
唯讀儲存器(rom):只能讀取,斷電資料不丟失。
快取記憶體(cache):有3種,l1,l2,l3 cache。它們位於cpu和記憶體之間,比記憶體的讀寫更快。
記憶體ic引腳配置圖中vcc和gnd表示電源,a0 - a9是位址訊號的引腳,d0 - d7表示的是控制訊號、rd和wr都 是好控制訊號,我用不同的顏色進行了區分。
將電源連線到vcc和gnd後,就可以對其他引腳傳遞 0和1的訊號,大多數情況下,+5v表示1, 0v表示0。
我們都知道記憶體是用來儲存資料,那麼這個記憶體ic中能儲存多少資料呢?
d0 - d7表示的是資料信 號,也就是說,一次可以輸入輸出8 bit = 1 byte的資料。a0 - a9是位址訊號共十個,表示可以指定 00000 00000 - 11111 11111共2的10次方=1024個位址。
每個位址都會存放1 byte的資料,因 此我們可以得出記憶體ic的容量就是1 kb。
如果我們使用的是512 mb的記憶體,這就相當於是512000(512 * 1000)個記憶體ic。當然,一台計 算機不太可能有這麼多個記憶體ic,然而,通常情況下,乙個記憶體ic會有更多的引腳,也就能儲存更多資料。
來詳細描述一下這個過程,假設我們要向記憶體ic中寫入ibyte的資料的話,它的過程是這樣的:為了便於記憶,我們把記憶體模型對映成為我們現實世界的模型,在現實世界中,記憶體的模型很想我們生活的樓房。在這個樓房中,1層可以儲存乙個位元組的資料,樓層號就是位址,下面是記憶體和樓層整合的模型圖。・ 首先給vcc接通+5v的電源,給gnd接通0v的電源,使用a0 - a9來指定資料的儲存場 所,然後再把資料的值輸入給d0 -d7的資料訊號,並把wr (write)的值置為1,執行完這些操作後,即可以向記憶體ic寫入資料
・ 讀出資料時,只需要通過a0 -a9的位址訊號指定資料的儲存場所,然後再將rd的值置為1即 可。
・ 圖中的rd和wr又被稱為控制訊號。其中當wr和rd都為0時,無法進行寫入和讀取操作。
下面看乙個實際例子的記憶體模型
//定義變數
char
a;
short
b;
long
c;
//變數賦值
前面講的只是記憶體的模型,下面要描述的是關於不同的cpu,不同的作業系統下的記憶體模型。
在這之前我們先看下不同的語言和編譯器為記憶體分配的記憶體區域
c語言:**區、初始化資料區、未初始化資料區、堆區和棧區5個部分不管是什麼語言,到最終都會被編譯成機器語言,然後cpu會一條一條指令的去執行,cpu和記憶體並不是直接交換資料的,它們之間還隔著乙個快取記憶體。快取記憶體是對程式設計師透明的,這意味在程式設計的時候是感知不到cpu的快取的存在的。一般情況下確實如此,但在,在某些特殊的情形下(多核多執行緒),就不能忽略快取的存在了。這其實是和快取的設計有關係,一般多處理器下的每個cpu都有乙個自己的快取,儲存在這個快取的資料是其它cpu是無法檢視的。c++: 堆、棧、自由儲存區、全域性/靜態儲存區和常量儲存區
c#: **區、常量區、靜態區、堆、棧
那麼就引出了乙個如何確保資料一致性的問題,其實這個跟當前web快取和資料庫一致性的問題類似,加一些讀寫鎖就能解決。
還有個問題是cpu為優化指令執行速度提公升效率會對指令進行指令重排。同樣的對於多執行緒就會有問題存在。
舉個例子,cpu-0將要執行兩條指令,分別是:那麼記憶體模型是什麼store x
load y
當cpu-0執行指令1的時候,發現這個變數x的當前狀態為shared,這意味著其它cpu也持有了x,因此根據快取一致性協議,cpu-0在修改x之前必須通知其它cpu,直到收到來自其它cpu的ack才會執行真正的修改x。
但是,事情沒有這麼簡單。現代cpu快取通常都有乙個store buffer,其存在的目的是,先將要store的變數記下來,注意此時並不真的執行store操作,然後待時機合適的時候再執行實際的store。
有了這個store buffer,cpu-0在向其它cpu發出disable訊息之後並不是幹等著,而是轉而執行指令2(由於指令1和指令2在cpu-0看來並不存在資料依賴)。這樣做效率是有了,但是也帶來了問題。
雖然我們在寫程式的時候,是先store x再執行load y,但是實際上cpu卻是先load y再store x,這個便是cpu亂序執行(reorder)的一種情況!
當你的程式要求指令1、2有邏輯上的先後順序時,cpu這樣的優化就是有問題的。但是,cpu並不知道指令之間蘊含著什麼樣的邏輯順序,在你告訴它之前,它只是假設指令之間都沒有邏輯關聯,並且盡最大的努力優化執行速度。
因此我們需要一種機制能告訴cpu:這段指令執行的順序是不可被重排的!做這種事的就是記憶體屏障(memory barrier)!
首先,殘酷的現實就是每個cpu設計都是不同的,每個cpu對指令亂序的程度也是不一樣的。比較保守的如x86僅會對store load亂序,但是一些優化激進的cpu(ps的power)會允許更多情況的亂序產生。如果目標是寫乙個跨平台多執行緒的程式,那麼勢必要了解每乙個cpu的細節,來插入確切的、足夠的記憶體屏障來保證程式的正確性。這是多麼的不科學啊!科學的做法應該是,我為乙個抽象的機器寫一套抽象的程式,然後在不同的平台下讓程式語言、編譯器來生成合適的記憶體屏障。因此,我們有了記憶體模型的概念。不同平台下的實現差別被統一的記憶體模型所隱藏,只需要根據這個抽象的記憶體模型來編寫程式即可,所以jvm, .net才應運而生。
計算機的簡單發展歷程 程式設計師的自身修養
早期的計算機沒有很複雜的圖形功能和處理能力,而且cpu的核心頻率也不是很高,跟記憶體的頻率一樣,所以將它們連線在 同一條匯流排上。但是i o裝置處理速度與cpu和記憶體相比還是慢很多,為了協調它們之間的速度,而不制約cpu效率,在每個裝置上配備乙個 i o控制器。隨著圖形化的作業系統普及,特別是3d...
計算機與程式設計師
計算機專業的學生,做一名程式設計師似乎是那麼的理所當然,每天面對著 除錯 修改 不可否認,在當接觸程式時,我是那麼肯定的認為自己要做好乙個程式設計師 或是所謂的工程師 可是這樣是不是自己把自己鎖進了一間由 堆砌的房間裡了,摸著鍵盤敲 自然有它枯燥的地方,當我接觸到3dmax 遊戲引擎 maya時 情...
程式設計師喝酒的計算機文化
程式設計師喝酒的計算機文化 程式設計師喝酒的計算機文化 大家喝的是啤酒。這時你入座了。你給自己倒了杯可樂,這叫低配置。你給自已倒了杯啤酒,這叫標準配置。你給自己倒了杯茶水,這茶的顏色還跟啤酒一樣,這叫木馬。你給自己倒了杯可樂,還滴了幾滴醋,不僅顏色跟啤酒一樣,而且不冒熱氣還有泡泡,這叫超級木馬。你的...