什麼是內存(一):存儲器層次結構

首先給你們講個段子:html

2015年開網吧,買了 DDR4 8g 內存條400多根,一根180塊,今年2017年,網吧賠了20多萬,昨天我把網吧電腦全賣了。內存條600一根,竟然賺回了我網吧的錢,感謝三星,感謝人民,感謝黨。。。java

今年以來,內存條價格暴漲,已經躍升爲新的新一代理財產品,因此今天就和你們討論一下內存的話題,主要內容就是在程序運行過程當中,內存的做用以及如何與CPU,OS交互。git

咱們先來討論:計算機的運行到底是在作什麼?來看一下經典的馮諾依曼結構。計算機科學雖然飛速發展了幾十年,可是依舊遵循馮諾依曼結構。程序員

馮諾依曼結構

馮諾依曼結構.jpg-23.2kB
圖1:馮諾依曼結構github

數學家馮諾依曼提出的 體系結構包含如下幾個要點:算法

  • 把程序自己看成數據來對待,程序和該程序處理的數據用一樣的方式儲存。
  • 計算機的數制採用二進制。
  • 計算機應該按照程序順序執行。

咱們根據這張圖進行思考就能夠獲得一個結論,所謂計算機處理任務,就是根據輸入內容,數據/程序從存儲器送往CPU進行處理,而後再將結果輸出。編程

關於程序與數據,數據就是一首MP3歌曲, 程序就是用來控制解析播放這首歌的代碼,從底層來說就是供CPU運行的指令.總之在計算機當中它們都是0和1,不過爲行文方便,咱們直接簡稱爲數據或程序或指令, 將它們理解爲同一個意思,畢竟它們都屬於0和1組成的流,這個能夠根據上下文來理解。數組

本文討論的主要內容,就是 存儲器部分,爲何計算機須要存儲器部分?這是顯而易見的,我寫好了程序,或者下載了一部電影,確定得有個地方放啊。這樣從此須要的時候,才能運行程序或者看電影啊。緩存

咱們思考一下,這個存儲器應該具有什麼樣的特色。服務器

  • 1.穩定,掉電不丟失數據:這個道理上面已經提過,辛辛苦苦下載個小電影,一關電腦數據都丟失了。這確定不行的。
  • 2.存儲容量大:就像誰也不嫌棄本身錢多,嫌棄自家房子太大。咱們既然存儲東西,那麼容量確定越大越好。
  • 3.讀寫速度快:拷貝個電視劇,速度那麼慢,真心累啊。
  • 4.價格便宜:新發布的iphone x我爲啥不買,由於它有一個缺點我沒法接受,那就是太貴了。一臺電腦賣一百萬,咱們誰又能買得起呢?
  • 5.體積小:這個也是理所固然的。

關於這個存儲器,咱們大概想出了一個理想的存儲器應該具有的的5個特色。
可是有句話說的好。理想很豐滿,顯示很骨感。一個屌絲在紙上列出了幾十條他理想女朋友的標準,可是他能如願嗎?

先說結論,徹底知足咱們理想條件的存儲器目前還沒發明出來呢。目前的半導體工業只能造出部分符合條件的存儲器,可是徹底知足以上幾條標準的,對不起,將來也許能作到,可是起碼目前作不到。

因此這也是目前計算機系統存儲器系統比較複雜的緣由,區分爲內存,硬盤,光盤等不一樣的存儲器,若是有個完美的符合咱們理想條件的存儲器,直接使用這種存儲器就行了。

先看看看咱們最多見的存儲設備:磁盤。足夠穩定;有電沒電都正常存儲;容量也較大;價格也能夠接受,因此磁盤是咱們最多見的存儲設備。

磁盤就是咱們存儲器的表明了。

爲了行文方便,文中直接將存儲器用磁盤來代替了,一來你們對磁盤比較熟悉,二來磁盤也是最多見的存儲設備。相似flash,SD卡,ROM等從廣義上來說,也能夠稱爲磁盤。由於它們的做用都是存儲數據,掉電後不丟失。(這在下面文章中也會討論到)

磁盤和硬盤什麼關係呢?實際上是同一個意思。硬盤是最多見的磁盤類型。在很早以前,計算機使用軟盤存儲數據,因此那種軟盤也被稱爲 磁盤,不過軟盤都早就被歷史淘汰了,(電腦硬盤分區從C盤開始,就是由於AB盤是以前軟盤的編號)。因此如今咱們說磁盤,直接理解成硬盤就行了。

在咱們軟件當中,有個概念叫作數據持久化,意思就是說將數據存儲起來,掉電以後不丟失,這其實就是存儲在磁盤上面。

因此如今咱們理解的計算機運行就是這樣一個過程:將數據從磁盤送往CPU,供CPU進行計算,並將結果輸出。

由於咱們這片文章就是 討論 內存,存儲等問題,因此關於 輸入設備,輸出設備之類的,就再也不涉及和討論。

而後咱們再簡短來討論CPU的發展歷史。

世界上第一臺計算機是1946年在美國誕生的ENIAC,當時CPU仍是使用笨重的電子管,後面的故事依次是貝爾實驗室發明了晶體管,TI的工程師又發明了集成晶體管,IBM研發成功首款使用集成電路的計算機,IBM360, 後面 就是仙童八叛徒與intel,AMD的故事了。這段很著名的IT故事,咱們再也不累述了。伴隨着世界上第一款商用處理器:Intel4004的出現,波瀾壯闊的摩爾定律開始了。

當時負責IBM 360 操做系統開發的那個項目經理,根據該項目經驗, 寫了一本經典著做《人月神話》,也有其餘參與者根據該項目經驗,立傳出書了,因此當時那批人都是大牛。

摩爾定律:當價格不變時,集成電路上可容納的元器件的數目,約每隔18-24個月便會增長一倍,性能也將提高一倍。

半導體行業開始騰飛了。CPU上集成的晶體管數量愈來愈多。 intel i9的製程工藝已經到了14nm。因此CPU的執行速度也愈來愈快。

固然,摩爾定律也快到盡頭了,根據量子力學,2nm是理論極限值。線寬不能再細了,低於2nm,隧穿效應就會產生干擾。

閒扯了一段CPU的發展歷史,想說明的是,如今的CPU集成度愈來愈高,速度也愈來愈快。每秒鐘能執行的指令也愈來愈多。(若是不知道指令,彙編之類的啥意思,看一下個人的另外一篇文章關於跨平臺的一些認識,不然下面的內容看着也有難度)。

CPU的做用就是去執行指令(固然,也包括輸出結果等,本文只討論和存儲器相關,因此不扯其餘的),而且儘量的以它的極限最高速度去執行指令,至於具體的執行過程,作過單片機或者學過微機原理的應該比較清楚。就是伴隨着時鐘週期滴滴答答的節奏,CPU踏着拍子來執行指令。

至於CPU的指令集,那就是Intel的架構師們的工做,總之,CPU認識這些指令,而且能執行運算。(別忘記了馮諾依曼體系結構那張圖)。對於這些指令,可是CPU採起了各類措施來加快執行過程(也能夠理解爲加快它的計算速度)。好比有如下幾種常見的措施:

  • 流水線(pipeline)技術:有電子廠打工經歷的讀者確定很熟悉這個流水線模式。CPU的流水線工做方式和工業生產上的流水線概念同樣。就是將一個指令的執行過程也分解爲多個步驟,CPU中的每一個電路只執行其中一個步驟,這樣前赴後繼加快執行速度。CPU中多個不一樣功能的電路單元組成一條指令處理流水線,而後將一條指令分紅幾個步驟後再由這些電路單元分別執行。在執行過程當中,指令源源不斷的送往CPU。讓每一個電路單元都不閒着,這樣就大大的加快了執行速度。
  • 超線程(Hyper-Threading)技術:對於超線程,百度百科的解釋我都沒看懂,可是大概原理就是這樣的。CPU在進行線程切換的時候,要執行 切換各類寄存器狀態等一些操做。把第一個線程的各類寄存器狀態寫回緩存中保存,而後把第二個線程的相關內容送到各類寄存器上。該過程必不可少,不然待會再將第一個線程切換回來時,不知道該線程的各個狀態, 那還怎麼接着繼續執行呢?也正由於如此,因此這個過程比較慢,大概須要幾萬個時鐘週期。因此後來作了這樣的設計,把每一個寄存器等都多作一個,就是多作一組寄存器(也包括一些其餘相關電路等),,CPU在執行A線程時,使用的第一組寄存器,切換到B線程,直接使用第二組寄存器,而後再切換A線程時,再使用第一組寄存器。,CPU就不用再傻傻的等着寄存器值的切換,線程切換隻須要幾個時鐘週期就夠了。對於普通的執行多任務的計算機,CPU線程切換是個很是頻繁的操做,因此使用該技術就會節省大量的時鐘週期。也就是至關於加快了CPU的執行速度。這就是CPU宣傳參數中所謂的四核八線程的由來,其實就是超線程技術。(每一個核多作一組寄存器等電路當然會佔用寶貴的空間,可是它帶來的優勢遠遠大於缺點)。
  • 超標量技術:CPU能夠在每一個時鐘週期內執行多個操做,能夠實行指令的並行運算。
  • 亂序執行: 咱們認爲程序都是順序執行的。可是在CPU層面上,指令的執行順序並不必定與它們在機器級程序(彙編)中的順序同樣。好比 a = b+c; d++;這兩個語句 不按照順序執行也不會影響最終結果。固然這只是在CPU執行指令的層面,在程序員們看來,依舊認爲程序是順序執行的。

前面扯了那麼多,就是爲了說明CPU的執行速度很快。雖然每條指令的執行時間須要幾個時鐘週期到幾十個時鐘週期不等。可是CPU採用了種種技術來加快執行過程。因此平均執行一條指令只須要一個週期。而如今CPU主頻都那麼高。好比i7 7700K主頻達到了 4.2G。這也就意味着,每一個core每秒鐘大約能夠執行4.2億條指令。那四個core呢?

CPU每秒鐘能夠執行幾億(甚至十幾億)條指令,因此它的執行速度真丫的的快啊

咱們討論完CPU如此快的執行速度,咱們再來講咱們常見的存儲設備-機械硬盤。

機械硬盤結構.jpg-90.4kB

圖2:機械硬盤結構

機械硬盤的結構就再也不具體的討論了。它讓我想起了民國電影中那種播放音樂的唱片機。

帶機械硬盤的電腦,在使用過程當中,若是機箱被摔了,可能後果很嚴重,就是由於可能會把機械硬盤的那個讀寫頭/傳動臂等機械結構摔壞。

機械硬盤容量很大(目前廣泛1T,2T),咱們的數據和程序是存儲在磁盤上的,因此CPU要想執行指令/數據,就要從存儲器,也就是磁盤上讀取, CPU一秒鐘能夠執行幾億條指令,可是相對之下,磁盤的讀寫速度就是慢如蝸牛。假設磁盤一秒鐘能夠讀取100條指令。那麼這中間就存在 巨大的速度差別。半導體行業發展了幾十年,CPU的執行速度一再飛速提高,奈何磁盤技術發展的太不給力了,CPU再快,但是磁盤嚴重拖後腿,那CPU就至關於工做嚴重不飽和,若是直接從磁盤上 來讀取數據,那麼CPU至關於 99.9999%的時間都在閒置着。

"假設磁盤一秒鐘能夠讀取100條指令。":帶有假設字樣的,具體數字都是隨便寫的。好比 磁盤讀寫速度天然有它的參數指標,不過咱們只是爲了說明問題, 因此能理解其中的道理就好。

磁盤廠商們也在努力研究,好比SSD(固態硬盤),它的速度就比 機械硬盤快了一二十倍吧。可是對於CPU的速度,這也是然並卵啊。(更況且SSD相比機械硬盤太貴了)

因此這就是個大問題。

咱們的目標就是執行任務時讓CPU全負荷的運行,爭取對於每個時鐘週期,CPU都不會閒置浪費。

這就像是老闆對咱們這些員工的但願同樣。老闆給咱們發工資, 那麼他就是但願咱們每一天的每一分每一秒都在努力幫公司幹活。不要有什麼任什麼時候間閒着。因此咱們要感謝勞動法,讓咱們天天工做八小時就夠了。畢竟咱們也是血肉之軀,也須要吃喝拉撒睡覺。

看到勞動法說天天工做八小時就夠了,程序猿們哭暈在廁所。

程序猿問科比:「你爲何這麼成功? 」
科比:「你知道洛杉磯凌晨四點是什麼樣子嗎? 」
程序猿:「不知道,通常那個時候我還沒下班呢,怎麼了?」
科比:「額…….」

經過上面的介紹,咱們就明白了計算機體系的主要矛盾,CPU太快了,而磁盤太慢了。因此它倆是不可以直接通訊的,咱們能夠加一層過分。這就是內存的做用。這就是幾百塊錢一根的內存條的做用和功能。

實際上,通常狀況下,內存的讀寫速度比磁盤快幾十萬倍左右。因此它終於夠資格和CPU直接通訊了。

這裏有張圖,咱們來看一下磁盤/內存,與CPU速度之間逐漸增大的差距(主要是CPU技術發展太迅猛了)。

磁盤DRAM和cpu速度之間逐漸增大的差距.png-122.1kB

圖三:磁盤DRAM和cpu速度之間逐漸增大的差距

因此如今程序執行過程是這樣的。CPU執行任務時,只與內存通訊,它從內存獲取指令/數據或寫回數據。內存再與磁盤通訊,內存從磁盤讀取數據/指令,或者內存將數據寫回磁盤。

提到添加過渡層。這其實和JVM的原理都是相似的。具體可參考個人另外一篇文章關於跨平臺的一些認識。也許這就是大道至簡吧。

存儲器層次結構

咱們這裏說的內存,主要是指主存。就是主板上插的內存條。它的讀寫速度比磁盤快了幾十萬倍。可是相對於CPU的速度依舊仍是慢。那麼主存和CPU之間,能夠繼續添加速度更快的過分層。因此intel i7的存儲器層次結構是這樣的。

一個存儲器層次結構的示例.jpg-111.3kB

圖4:一個存儲器層次結構的示例

前面扯了那麼多篇幅,就是告訴你,咱們爲何須要內存(主存),那麼理解了主存,天然也就理解了L3,L2,L1等各級緩存存在的意義。對於現代的計算機系統,在CPU與磁盤/主存之間,加了多層過分層。

嚴格來說,應該叫CPU的算術邏輯單元(ALU),可是簡單的直接說CPU,你們確定也能聽得懂。

實際上這是一種緩存思想。好比,本地磁盤也至關於 遠方服務器的緩存。由於咱們從網上下載數據/文件時,速度明顯比從本地磁盤讀取要慢。

通常狀況下,L5磁盤與L4主存速度相差幾十萬倍, 而L3-L0之間,它們每級緩存的速度差別大概是10倍。

咱們是拿i7處理器來作例子,它有三級緩存,像低端一些的處理器,好比i3,只有兩級緩存,可是道理是相同的。本文當中,都是拿i7的存儲器層次來作例子。

明白一點。CPU執行速度實在太快了,一秒鐘執行幾億/十幾億條指令,CPU幹活乾脆利落,那麼存儲器就要千方百計的用最快的速度把指令/數據 送給CPU去運行。不然CPU幹活再快,又有什麼意義呢。

基本思想已經理解了。那麼咱們就開始具體討論細節問題。

RAM,ROM,總線等

看看上面那幅圖,什麼SRAM,DRAM,還有咱們前面講的SSD,Flash,機械硬盤等,還有下面要討論的總線(BUS),因此咱們先來討論一些基礎硬件知識.

首先,他們都屬於存儲器,存儲器分爲兩類:

  1. 易失性(volatile)存儲器:包括內存,SRAM,DRAM等,特色是讀寫速度很快,掉電了數據會丟失,價格貴,而且存儲容量較小。
  2. 非易失性(nonvolatile)存儲器:包括磁盤,Flash,光盤,機械硬盤,SSD等,與易失性存儲器相比,它們讀寫速度很慢,可是掉電不丟失數據,存儲容量比較大,價格也便宜。
  • RAM(Random-Access Memory):隨機訪問存儲器。易失性存儲器。也能夠訪問兩類:SRAM(靜態的)和DRAM(動態的),而且SRAM的讀寫速度比DRAM更快,價格也更貴。在上圖中也能夠看到, SRAM作L1-L3級緩存,而DRAM作L4級的主存。
  • ROM(read-only memory):只讀存儲器,非易失性存儲器。這個名字容易讓人產生誤解,它既能夠讀,也能夠寫,稱之爲read-only只是歷史緣由。

ROM相比於RAM,容量更大,價格便宜,讀寫速度則比較慢。

  • 閃存(Flash memory):非易失性存儲器。SSD,SD卡都屬於Flash技術,若是從概念上來說,他們都屬於ROM,這類存儲器常常用在手機,相機等設備上。而機械硬盤經常使用在我的計算機,服務器上。

其實我覺的把 Flash,ROM等都叫作磁盤,也沒什麼錯。畢竟它們的做用和概念都是類似的,區別只是他們各自使用的半導體技術不一樣。Flash芯片等基於集成芯片的存儲器讀寫速度比機械硬盤快,不過(相同容量下)價格也比後者貴。而它們相比於SRAM,DRAM則很是慢了,因此後者理解爲內存便可。

"圖4:一個存儲器層次結構的示例",越往上,讀寫速度越快,價格更貴,存儲容量也越小。(淘寶上搜搜8G的內存條,256G的SSD,1T的機械硬盤都是什麼價格就明白了)。像L0 寄存器,每一個寄存器只能存儲一個字長的內容,可是CPU讀寫取寄存器耗費的時鐘週期爲0個。這是最快的速度。

另外,咱們在電腦主板上能夠看到內存條(L4主存)。硬盤(L5),可是卻沒看到L3-L0。緣由很簡單,他們都是集成在CPU芯片內部的。

咱們知道了存儲器的層級結構,下面還有一個問題,就是怎麼把硬盤,內存條之類的鏈接起來進行通訊呢,這就是 總線(Bus)了。

一個典型系統的硬件組成.jpg-91.1kB

圖6:一個典型系統的硬件組成

上圖存在三條總線,IO總線,存儲器總線(一般稱爲內存總線),系統總線。在主板上,就是那一排排的32/64根並行的導線。這些導線用來鏈接CPU,內存,硬盤,以其餘外圍設備。CPU與存儲器,輸入輸出設備等通訊,都是經過總線。不一樣總線的速度也有差別。

CPU要經過I/O橋(就是主板的北橋/南橋芯片組)與外圍設備鏈接,由於CPU的主頻過高了,它的時鐘週期一秒鐘震盪幾億次,外圍設備的時鐘週期都較慢,因此他們不能直接通訊。

本文是討論軟件的,因此硬件部分就一筆帶過,讀者知道有這回事就ok了。總線上攜帶地址,數據和控制信號, 如何區分不一樣信號,分辨它與哪一個外圍設備通訊,這就是另一個問題了。

無論中間怎麼加緩存,數據從硬盤到內存的速度就是那麼慢,那麼這些緩存意義何在?

有些讀者腦子轉的比較快,可能想到了這樣一個問題。

無論你中間怎麼加緩存,也無論中間的什麼SRAM,DRAM的讀寫速度有多快,可是磁盤的讀寫速度就是那麼慢,因此磁盤與主存之間的交互速度很慢。CPU歸根到底須要向磁盤讀寫數據。整個環節速度瓶頸就是在磁盤那裏,這個根本快不了,那麼加那麼多級緩存,意義有何在呢?

這是一個好問題啊。 下面讓咱們繼續討論。

咱們來看看,CPU如何讀取磁盤中的一個數據。

讀一個磁盤扇區-1.png-27.6kB

讀一個磁盤扇區-2.png-138.1kB

圖7:讀一個磁盤扇區

網上找的圖片不是很清楚,注意每張圖中的黑線。步驟分三部:

  1. CPU 將相關的命令和地址,經過系統總線和IO總線傳遞給磁盤,發起一個磁盤讀。
  2. 磁盤控制器將相關的地址解析,並經過IO總線與內存總線將數據傳給內存。
  3. 第2步完成以後,磁盤控制器向CPU發送一箇中斷信號。(學電子的同窗應該很清楚中斷是什麼)。這時CPU就知道了,數據已經發送到內存了。

第二步磁盤操做很慢,可是在第一步CPU發出信號後。可是第二步和第三部時,CPU根本不參與。第二步很耗時,因此CPU在第一步發出信號後,就去在幹其餘事情啊。(切換到另外一個線程)。因此此時的CPU依舊沒有閒着。而待第三步時,經過中斷,硬盤主動發信號給CPU,你須要的數據已經發送到內存了,而後此時它能夠將線程再切換回來,接着執行這個該線程的任務。

除了多線程切換,避免CPU閒置浪費,還有一點。
我先問一個問題。

//@author :www.yaoxiaowen.com
int main(){
    //咱們執行任務的代碼
    return 0;
}

對於一個應用/進程而言,它都應該有一個入口。(雖然不必定須要咱們直接寫main函數)。入口函數內部就是咱們的任務代碼,任務代碼執行完了這個應用/進程也就結束了。這個很好理解,好比測試工程師寫的一個測試case。跑完了這個任務就結束了。

可是 有些程序,好比一個 app,你打開了這個app。不作任何操做。這個界面會一直存在,也不會消失。思考一下這是爲何。由於這個app進程確定也要有一個main入口。 main裏面的任務代碼執行完了,就應該結束了。而一個程序的代碼/指令數目確定是有限的。但該app在咱們不主動退出狀況下,卻不會主動結束。

因此這個app進程的入口main來說,實際上是這樣的。

//@author :www.yaoxiaowen.com
int main(){
    boolean flag  = true;
    while (flag){
        //咱們執行任務的代碼
    }
    return 0;
}

而且不只如此,在一個程序內部,也有大量的for,while等循環語句。
那麼當咱們把這些相關的代碼指令送到了主存,或者更高一級的緩存時,那麼CPU在執行這些指令時,存取速度天然快了不少。

在執行一個程序時,啓動階段比較慢,由於須要從磁盤讀取數據。(而CPU在這個階段也沒閒置浪費,它會進行線程切換執行其餘任務)。 可是數據被送往內存以後,它執行起來就會快多了,而且伴隨着執行過程,還可能愈來愈快,由於這些數據,有可能被一級一級的向上送,從L4,送到L3,再送到L2,L1

so,上述那個問題的答案,已經解釋的比較清楚了吧。

局部性原理(Principle of locality)

locality對於硬件和軟件系統的設計和性能都有着重要的影響。對於咱們理解存儲器的層次結構也必不可缺。

程序傾向於引用臨近於與其餘最近引用過的數據項的數據項。或者最近引用過的數據項自己。這種傾向性,咱們稱之爲局部性原理。它一般有如下兩種形式:

  • 時間局部性(temporal locality):被引用過一次的存儲器位置的內容在將來會被屢次引用。
  • 空間局部性(spatial locality):若是一個存儲器位置的內容被引用,那麼它附近的位置也很大機率會被引用。

通常而言,有良好局部性的程序比局部性差的程序運行的更快。 現代計算機系統的各個層次,從硬件到操做系統、再到應用程序,它們的設計都利用了局部性。

固然,光說理論的東西比較玄乎。咱們來看實際的例子。

//@author www.yaoxiaowen.com
int sum1(int array[N])
{
    int i, sum = 0;
    for(i = 0; i < N; i++)
        sum += array[i];
    return sum;
}

在這個程序中,變量sum,i在每次循環迭代時被引用一次,所以對sumi來講,有較好的時間局部性。
對變量array來講,它是一個int類型數組,循環時按順序訪問array,由於一個C數組在內存中是佔用連續的內存空間。於是的較好的空間局部性,

再來看一個例子:

//@author www.yaoxiaowen.com
int sum2(int array[M][N])
{
    int i, j, sum = 0;
    for(i = 0; i < M; i++){
        for(j = 0; j < N; j++)
            sum += array[j][i];
    }   
    return sum;
}

這是一個空間局部性不好的程序。
假設這個數組是array[3][4],由於C數組在內存中是按行順序來存放的。因此sum2對每一個數組元素的訪問順序成了這樣:0, 4, 8, 1, 5, 9…… 7, 11。因此它的空間局部性不好。

可是幸運的是,通常狀況下軟件編程自然就是符合局部性原理的。好比程序的循環結構。

假設CPU須要讀取一個值,int var,而var在L4主存上,那麼該值會被依次向上送,L4->L3->L2,可是這個傳遞的過程並非單純的只傳遞var四個字節的內容,而是把var所在的內存塊(block),依次向上傳遞,爲何要傳遞block?由於根據局部性原理,咱們認爲,與var值相鄰的值,將來也會被引用。

存儲器的層次結構,數據進行傳送時,是以block(塊)爲單位傳送的。在整個層次結構上,越往上,block越小而已。

存儲器層次結構中的緩存

洋洋灑灑的扯了那麼多,我相對於所謂的 存儲器層次結構讀者應該有一個基本的認識,有些地方介紹的 不夠嚴謹,可是本文的目的也就是讓你們理解基本思想。

歸根到底,它就是一個緩存(caching)的思想,而且其實不復雜,

咱們作app開發時,對於app中活動頁面等,都是後臺發給咱們圖片url,咱們下載後才顯示在app上,這時咱們總要使用 Glide,Picasso 等圖片緩存框架來把下載好的圖片緩存在手機本地存儲上。這樣下次打開app時,若是這個圖片連接沒有改變,咱們就直接拿手機本地緩存的圖片來進行顯示,而不用再從服務器上下載了。若是圖片連接改變了,則從新下載。爲何要這麼作?由於從服務器上下載比較慢,而手機本地存儲(ROM)中讀取就會快不少。

這個時候能夠再回頭看看"圖4:一個存儲器層次結構的示例"

下面這張圖和這段文字來自《深刻理解計算機系統》(CSAPP),你們能夠有個更嚴謹和細節的認識。

存儲器層次結構中基本的緩存原理.png-58.5kB

圖8:存儲器層次結構中基本的緩存原理

存儲器層次結構的中心思想:位於k層的更快更小的存儲設備做爲位於k+1層得更大更慢的存儲設備的緩存;數據老是以塊大小爲傳送單元(transfer unit)在第k層和第k+1層之間來回拷貝的;任何一對相鄰的層次之間傳送的塊大小是固定的,即每一級緩存的塊大小是固定的。可是其它的層次對之間能夠有不一樣的塊大小。

當程序須要第k+1層的某個數據對象d時,它首先在當前存儲在第k層的一個塊中查找d。若是d恰好在k層,那麼就是緩存命中。若是第k層中沒有緩存數據對象d,那麼就是緩存命不中。當緩存不命中發生時,第k層的緩存從第k+1層 緩存中取出包含d的那個塊,若是第k層的緩存已經滿了的話,可能會覆蓋現存的一個塊。(覆蓋策略可使用常見的LRU算法)。

volatile 關鍵字

在java和C當中,有一個volatile關鍵字(其餘語言估計也有),它的做用就是在多線程時保證變量的內存可見性,可是具體怎麼理解呢?

咱們在"圖4:一個存儲器層次結構的示例"中,說的緩存結構其實對於一個單核CPU而言的,好比 對於 一個四核三級緩存的CPU,它的緩存結構是這樣的。

多核處理器緩存結構.jpg-47.8kB

圖9:多核處理器緩存結構

咱們能夠看到L3是四個核共有的,可是L2,L1實際上是每一個核私有的,若是我有一個變量var,它會被兩個線程同時讀取,這兩個線程在兩個核上並行執行,由於咱們的緩存原理,這個var可能分別在兩個核的 L2L1緩存,這樣讀取速度最快,可是該var值可能就分別被這兩個核分別修改爲不一樣的值, 最後將值回寫到L3L4主存,此時就會發生bug了。

因此volatile關鍵字就是預防這種狀況,對於被volatile修飾的的變量,每次CPU須要讀取時,都至少要從L3讀取,而且CPU計算結束後,也馬上回寫到L3中,這樣讀寫速度雖然減慢了一些,可是避免了該值在每一個core的私有緩存中單獨操做而其餘核不知道。

下篇文章引言

本篇是"什麼是內存"系列第一篇文章,下一篇文章會討論關於內存的另外一個重要方面,兩篇文章加起來,相信你們會對內存有一個全面的,全新的認識。
這裏請你們思考如下幾個問題。

  • 無論什麼程序,最後的直接/間接的編譯結果都是0和1,(咱們直接理解爲彙編)。(這點不知道的,歡迎閱讀個人另外一篇文章關於跨平臺的一些認識),好比這句彙編代碼:mov eax,0x123456;它的意思是將內存0x123456處的內容送往eax這個寄存器。各個應用的數據共同存在內存中的。假設有一個音樂播放器應用的彙編代碼中,引用了0x123456這個內存地址。可是同時運行的應用有不少,那其餘應用也徹底有可能引用 0x123456這個地址。那爲何居然沒起衝突和錯誤呢?
  • 進程是計算機領域最重要的概念之一,什麼是進程?進程是關於某次數據集合的一次運行活動, 是運行在它本身地址空間的一段自包容程序, 解釋的通俗的點, 一個程序在運行時,咱們會獲得一個假象,該進程好像是獨佔地使用CPU和內存,CPU是沒有間斷地一條接一條的執行該程序的指令,全部的內存空間都是供該進程的代碼和數據分配使用的。(這點不嚴謹,其實內存還有一部分要分給內核kernel)。提及來,這個程序就好像獲得了全世界同樣。,CPU是個人,內存也所有個人,妹子們仍是個人。固然這是假象而已。可是這些假象又是怎麼作到的呢?
  • 程序中都會引用庫API,好比每一個C程序都要引用stdio.h庫的printf(),在程序運行時,庫代碼也要被加入到內存,這麼多程序都引用了這個庫,難道我內存中須要加不少份嗎?這天然不可能,那麼庫代碼又是怎麼被全部進程共享的呢?

下篇文章將會給你們解釋這些問題,而且這些問題的答案是很是簡單的。相信你們看了會有恍然大悟的感受,敬請期待。


1109補充
什麼是內存(二):虛擬內存已經發表。歡迎指點批評。


做者: www.yaoxiaowen.com

博客地址: www.cnblogs.com/yaoxiaowen/

github: https://github.com/yaowen369

歡迎對於本人的博客內容批評指點,若是問題,可評論或郵件(yaowen369@gmail.com)聯繫

歡迎轉載,轉載請註明出處.謝謝

相關文章
相關標籤/搜索