1. 程式人生 > >cache一致性入門級解答

cache一致性入門級解答

我計劃寫一些關於多核場景下資料組織的文章。寫了第一篇,但我很快意識到有大量的基礎知識我首先需要講一下。在本文中,我就嘗試闡述這些知識。

快取(Cache)

本文是關於CPU快取的快速入門。我假設你已經有了基本概念,但你可能不熟悉其中的一些細節。(如果你已經熟悉了,你可以忽略這部分。)

在現代的CPU(大多數)上,所有的記憶體訪問都需要通過層層的快取來進行。也有些例外,比如,對對映成記憶體地址的I/O口、寫合併(Write-combined)記憶體,這些訪問至少會繞開這個流程的一部分。但這兩者都是罕見的場景(意味著絕大多數的使用者態程式碼都不會遇到這兩種情況),所以在本文中,我將忽略這兩者。

CPU的讀/寫(以及取指令)單元正常情況下甚至都不能直接訪問記憶體——這是物理結構決定的;CPU都沒有管腳直接連到記憶體。相反,CPU和一級快取(L1 Cache)通訊,而一級快取才能和記憶體通訊。大約二十年前,一級快取可以直接和記憶體傳輸資料。如今,更多級別的快取加入到設計中,一級快取已經不能直接和記憶體通訊了,它和二級快取通訊——而二級快取才能和記憶體通訊。或者還可能有三級快取。你明白這個意思就行。

快取是分“段”(line)的,一個段對應一塊儲存空間,大小是32(較早的ARM、90年代/2000年代早期的x86和PowerPC)、64(較新的ARM和x86)或128(較新的Power ISA機器)位元組。每個快取段

知道自己對應什麼範圍的實體記憶體地址並且在本文中,我不打算區分物理上的快取段和它所代表的記憶體,這聽起來有點草率,但是為了方便起見,還是請熟悉這種提法。具體地說,當我提到“快取段”的時候,我就是指一段和快取大小對齊的記憶體,不關心裡面的內容是否真正被快取進去(就是說儲存在任何級別的快取中)了。

當CPU看到一條讀記憶體的指令時,它會把記憶體地址傳遞給一級資料快取(或可戲稱為L1D$,因為英語中“快取(cache)”和“現金(cash)”的發音相同)。一級資料快取會檢查它是否有這個記憶體地址對應的快取段。如果有,它會把整個快取段從記憶體(或者從更高一級的快取,如果有的話)中載入進來。是的,一次載入整個快取段,這是基於這樣一個假設:記憶體訪問傾向於本地化(localized),如果我們當前需要某個地址的資料,那麼很可能我們馬上要訪問它的鄰近地址。一旦快取段被載入到快取中,讀指令就可以正常進行讀取。

如果我們只處理讀操作,那麼事情會很簡單,因為所有級別的快取都遵守以下規律,我稱之為:

基本定律:在任意時刻,任意級別快取中的快取段的內容,等同於它對應的記憶體中的內容

一旦我們允許寫操作,事情就變得複雜一點了。這裡有兩種基本的寫模式:直寫(write-through)和回寫(write-back)。直寫更簡單一點:我們透過本級快取,直接把資料寫到下一級快取(或直接到記憶體)中,如果對應的段被快取了,我們同時更新快取中的內容(甚至直接丟棄),就這麼簡單。這也遵守前面的定律:快取中的段永遠和它對應的記憶體內容匹配。

回寫模式就有點複雜了。快取不會立即把寫操作傳遞到下一級而是僅修改本級快取中的資料,並且把對應的快取段標記為“髒”段。髒段會觸發回寫,也就是把裡面的內容寫到對應的記憶體或下一級快取中。回寫後,髒段又變“乾淨”了。當一個髒段被丟棄的時候,總是先要進行一次回寫。

回寫定律:當所有的髒段被回寫後,任意級別快取中的快取段的內容,等同於它對應的記憶體中的內容。

換句話說,回寫模式的定律中,我們去掉了“在任意時刻”這個修飾語,代之以弱化一點的條件:要麼快取段的內容和記憶體一致(如果快取段是乾淨的話),要麼快取段中的內容最終要回寫到記憶體中(對於髒快取段來說)。

直接模式更簡單,但是回寫模式有它的優勢:它能過濾掉對同一地址的反覆寫操作,並且,如果大多數快取段都在回寫模式下工作,那麼系統經常可以一下子寫一大片記憶體,而不是分成小塊來寫,回寫的效率更高。

有些(大多數是比較老的)CPU只使用直寫模式,有些只使用回寫模式,還有一些,一級快取使用直寫而二級快取使用回寫。這樣做雖然在一級和二級快取之間產生了不必要的資料流量,但二級快取和更低階快取或記憶體之間依然保留了回寫的優勢。我想說的是,這裡涉及到一系列的取捨問題,且不同的設計有不同的解決方案。沒有人規定各級快取的大小必須一致。舉個例子,我們會看到有CPU的一級快取是32位元組,而二級快取卻有128位元組。

為了簡化問題,我省略了一些內容:快取關聯性(cache associativity),快取組(cache sets),使用分配寫(write-allocate)還是非分配寫(上面我描述的直寫是和分配寫相結合的,而回寫是和非分配寫相結合的),非對齊的訪問(unaligned access),基於虛擬地址的快取。如果你感興趣,所有這些內容都可以去查查資料,但我不準備在這裡講了。

一致性協議(Coherency protocols)

【核心問題】只要系統只有一個CPU核在工作,一切都沒問題。如果有多個核,每個核又都有自己的快取,那麼我們就遇到問題了:如果某個CPU快取段中對應的記憶體內容被另外一個CPU偷偷改了,會發生什麼?

好吧,答案很簡單:什麼也不會發生。這很糟糕。因為如果一個CPU快取了某塊記憶體,那麼在其他CPU修改這塊記憶體的時候,我們希望得到通知。我們擁有多組快取的時候,真的需要它們保持同步。或者說,系統的記憶體在各個CPU之間無法做到與生俱來的同步,我們實際上是需要一個大家都能遵守的方法來達到同步的目的。

注意,這個問題的根源是我們擁有多組快取,而不是多個CPU核。我們也可以這樣解決問題,讓多個CPU核共用一組快取:也就是說只有一塊一級快取,所有處理器都必須共用它。在每一個指令週期,只有一個幸運的CPU能通過一級快取做記憶體操作,執行它的指令。

這本身沒問題。唯一的問題就是太慢了,因為這下處理器的時間都花在排隊等待使用一級快取了(並且處理器會做大量的這種操作,至少每個讀寫指令都要做一次)。我指出這一點是因為它表明了問題不是由多核引起的,而是由多快取引起的。我們知道了只有一組快取也能工作,只是太慢了,接下來最好就是能做到:使用多組快取,但使它們的行為看起來就像只有一組快取那樣。快取一致性協議就是為了做到這一點而設計的。就像名稱所暗示的那樣,這類協議就是要使多組快取的內容保持一致。

快取一致性協議有多種,但是你日常處理的大多數計算機裝置使用的都屬於“窺探(snooping)”協議,這也是我這裡要講的。(還有一種叫“基於目錄的(directory-based)”協議,這種協議的延遲性較大,但是在擁有很多個處理器的系統中,它有更好的可擴充套件性。)

“窺探”背後的基本思想是,所有記憶體傳輸都發生在一條共享的總線上,而所有的處理器都能看到這條匯流排:快取本身是獨立的,但是記憶體是共享資源,所有的記憶體訪問都要經過仲裁(arbitrate):同一個指令週期中,只有一個快取可以讀寫記憶體。窺探協議的思想是,快取不僅僅在做記憶體傳輸的時候才和匯流排打交道,而是不停地在窺探總線上發生的資料交換,跟蹤其他快取在做什麼。所以當一個快取代表它所屬的處理器去讀寫記憶體時,其他處理器都會得到通知,它們以此來使自己的快取保持同步。只要某個處理器一寫記憶體,其他處理器馬上就知道這塊記憶體在它們自己的快取中對應的段已經失效。

在直寫模式下,這是很直接的,因為寫操作一旦發生,它的效果馬上會被“公佈”出去。但是如果混著回寫模式,就有問題了。因為有可能在寫指令執行過後很久,資料才會被真正回寫到實體記憶體中——在這段時間內,其他處理器的快取也可能會傻乎乎地去寫同一塊記憶體地址,導致衝突。在回寫模型中,簡單把記憶體寫操作的資訊廣播給其他處理器是不夠的,我們需要做的是,在修改本地快取之前,就要告知其他處理器。搞懂了細節,就找到了處理回寫模式這個問題的最簡單方案,我們通常叫做MESI協議(譯者注:MESI是Modified、Exclusive、Shared、Invalid的首字母縮寫,代表四種快取狀態,下面的譯文中可能會以單個字母指代相應的狀態)。

MESI以及衍生協議

本節叫做“MESI以及衍生協議”,是因為MESI衍生了一系列緊密相關的一致性協議。我們先從原生的MESI協議開始:MESI是四種快取段狀態的首字母縮寫,任何多核系統中的快取段都處於這四種狀態之一。我將以相反的順序逐個講解,因為這個順序更合理:

失效(Invalid)快取段,要麼已經不在快取中,要麼它的內容已經過時。為了達到快取的目的,這種狀態的段將會被忽略。一旦快取段被標記為失效,那效果就等同於它從來沒被載入到快取中。

共享(Shared)快取段,它是和主記憶體內容保持一致的一份拷貝,在這種狀態下的快取段只能被讀取,不能被寫入。多組快取可以同時擁有針對同一記憶體地址的共享快取段,這就是名稱的由來。

獨佔(Exclusive)快取段,和S狀態一樣,也是和主記憶體內容保持一致的一份拷貝。區別在於,如果一個處理器持有了某個E狀態的快取段,那其他處理器就不能同時持有它,所以叫“獨佔”。這意味著,如果其他處理器原本也持有同一快取段,那麼它會馬上變成“失效”狀態。

已修改(Modified)快取段,屬於髒段,它們已經被所屬的處理器修改了。如果一個段處於已修改狀態,那麼它在其他處理器快取中的拷貝馬上會變成失效狀態,這個規律和E狀態一樣。此外,已修改快取段如果被丟棄或標記為失效,那麼先要把它的內容回寫到記憶體中——這和回寫模式下常規的髒段處理方式一樣。

如果把以上這些狀態和單核系統中回寫模式的快取做對比,你會發現I、S和M狀態已經有對應的概念:失效/未載入、乾淨以及髒的快取段。所以這裡的新知識只有E狀態,代表獨佔式訪問。這個狀態解決了“在我們開始修改某塊記憶體之前,我們需要告訴其他處理器”這一問題:只有當快取段處於E或M狀態時,處理器才能去寫它,也就是說只有這兩種狀態下,處理器是獨佔這個快取段的。當處理器想寫某個快取段時,如果它沒有獨佔權,它必須先發送一條“我要獨佔權”的請求給匯流排,這會通知其他處理器,把它們擁有的同一快取段的拷貝失效(如果它們有的話)。只有在獲得獨佔權後,處理器才能開始修改資料——並且此時,這個處理器知道,這個快取段只有一份拷貝,在我自己的快取裡,所以不會有任何衝突。

反之,如果有其他處理器想讀取這個快取段(我們馬上能知道,因為我們一直在窺探匯流排),獨佔或已修改的快取段必須先回到“共享”狀態。如果是已修改的快取段,那麼還要先把內容回寫到記憶體中。

MESI協議是一個合適的狀態機,既能處理來自本地處理器的請求,也能把資訊廣播到總線上。我不打算講更多關於狀態圖的細節以及不同的狀態轉換型別。如果你感興趣的話,可以在關於硬體架構的書中找到更多的深度內容,但對於本文來說,講這些東西有點過了。作為一個軟體開發者,你只要理解以下兩點,就大有可為:

第一,在多核系統中,讀取某個快取段,實際上會牽涉到和其他處理器的通訊,並且可能導致它們發生記憶體傳輸。寫某個快取段需要多個步驟:在你寫任何東西之前,你首先要獲得獨佔權,以及所請求的快取段的當前內容的拷貝(所謂的“帶許可權獲取的讀(Read For Ownership)”請求)。

第二,儘管我們為了一致性問題做了額外的工作,但是最終結果還是非常有保證的。即它遵守以下定理,我稱之為:

MESI定律:在所有的髒快取段(M狀態)被回寫後,任意快取級別的所有快取段中的內容,和它們對應的記憶體中的內容一致。此外,在任意時刻,當某個位置的記憶體被一個處理器載入入獨佔快取段時(E狀態),那它就不會再出現在其他任何處理器的快取中。

注意,這其實就是我們已經講過的回寫定律加上獨佔規則而已。我認為MESI協議或多核系統的存在根本沒有弱化我們現有的記憶體模型。

好了,至此我們(粗略)講了原生MESI協議(以及使用它的CPU,比如ARM)。其他處理器使用MESI擴充套件後的變種。常見的擴充套件包括“O”(Owned)狀態,它和E狀態類似,也是保證快取間一致性的手段,但它直接共享髒段的內容,而不需要先把它們回寫到記憶體中(“髒段共享”),由此產生了MOSEI協議。還有MERSI和MESIF,這兩個名字代表同一種思想,即指定某個處理器專門處理針對某個快取段的讀操作。當多個處理器同時擁有某個S狀態的快取段的時候,只有被指定的那個處理器(對應的快取段為R或F狀態)才能對讀操作做出迴應,而不是每個處理器都能這麼做。這種設計可以降低匯流排的資料流量。當然你可以同時加入R/F狀態和O狀態,或者更多的狀態。這些都屬於優化,沒有一種會改變基本定律,也沒有一種會改變MESI協議所確保的結果。

我不是這方面的專家,很有可能有系統在使用其他協議,這些協議並不能完全保證一致性,不過如果有,我沒有注意到它們,或者沒有看到有什麼流行的處理器在使用它們。所以為了達到我們的目的,我們真的就可以假設一致性協議能保證快取的一致性。不是基本一致,不是“寫入一會兒後才能保持一致”——而是完全的一致。從這個層面上說,除非硬體有問題,記憶體的狀態總是一致的。用技術術語來說,MESI以及它的衍生協議,至少在原理上,提供了完整的順序一致性(sequential consistency),在C++ 11的記憶體模型中,這是最強的一種確保記憶體順序的模型。這也引出了問題,為什麼我們需要弱一點的記憶體模型,以及“什麼時候會用到它們”?

記憶體模型

不同的體系結構提供不同的記憶體模型。到本文寫作的時候為止,ARM和POWER體系結構的機器擁有相對較弱的記憶體模型:這類CPU在讀寫指令重排序(reordering)方面有相當大的自由度,這種重排序有可能會改變程式在多核環境下的語義。通過“記憶體屏障(memory barrier)”,程式可以對此加以限制:“重排序操作不允許越過這條邊界”。相反,x86則擁有較強的記憶體模型。

我不打算在這裡深入到記憶體模型的細節中,這很容易陷入堆砌技術術語中,而且也超出了本文的範圍。但是我想說一點關於“他們如何發生”的內容——也就是,弱記憶體模型如何保證正確性(相比較於MESI協議給快取帶來的順序一致性),以及為什麼。當然,一切都歸結於效能。

規則是這樣的:如果滿足下面的條件,你就可以得到完全的順序一致性:第一,快取一收到匯流排事件,就可以在當前指令週期中迅速做出響應。第二,處理器如實地按程式的順序,把記憶體操作指令送到快取,並且等前一條執行完後才能傳送下一條。當然,實際上現代處理器一般都無法滿足以上條件:

快取不會及時響應匯流排事件。如果總線上發來一條訊息,要使某個快取段失效,但是如果此時快取正在處理其他事情(比如和CPU傳輸資料),那這個訊息可能無法在當前的指令週期中得到處理,而會進入所謂的“失效佇列(invalidation queue)”,這個訊息等在佇列中直到快取有空為止。

處理器一般不會嚴格按照程式的順序向快取傳送記憶體操作指令。當然,有亂序執行(Out-of-Order execution)功能的處理器肯定是這樣的。順序執行(in-order execution)的處理器有時候也無法完全保證記憶體操作的順序(比如想要的記憶體不在快取中時,CPU就不能為了載入快取而停止工作)。

寫操作尤其特殊,因為它分為兩階段操作:在寫之前我們先要得到快取段的獨佔權。如果我們當前沒有獨佔權,我們先要和其他處理器協商,這也需要一些時間。同理,在這種場景下讓處理器閒著無所事事是一種資源浪費。實際上,寫操作首先發起獲得獨佔權的請求,然後就進入所謂的由“寫緩衝(store buffer)”組成的佇列(有些地方使用“寫緩衝”指代整個佇列,我這裡使用它指代佇列的一條入口)。寫操作在佇列中等待,直到快取準備好處理它,此時寫緩衝就被“清空(drained)”了,緩衝區被回收用於處理新的寫操作。

這些特性意味著,預設情況下,讀操作有可能會讀到過時的資料(如果對應失效請求還等在佇列中沒執行),寫操作真正完成的時間有可能比它們在程式碼中的位置晚,一旦牽涉到亂序執行,一切都變得模稜兩可。回到記憶體模型,本質上只有兩大陣營:

在弱記憶體模型的體系結構中,處理器為了開發者能寫出正確的程式碼而做的工作是最小化的,指令重排序和各種緩衝的步驟都是被正式允許的,也就是說沒有任何保證。如果你需要確保某種結果,你需要自己插入合適的記憶體屏障——它能防止重排序,並且等待佇列中的操作全部完成。

使用強一點的記憶體模型的體系結構則會在內部做很多記錄工作。比如,x86會跟蹤所有在等待中的記憶體操作,這些操作都還沒有完全完成(稱為“退休(retired)”)。它會把它們的資訊儲存在晶片內部的MOB(“memory ordering buffer”,記憶體排序緩衝)。x86作為部分支援亂序執行的體系結構,在出問題的時候能把尚未“退休”的指令撤銷掉——比如發生頁錯誤(page fault),或者分支預測失敗(branch mispredict)的時候。我已經在我以前的文章“好奇地說”中提到過一些細節,以及和記憶體子系統的一些互動。主旨是x86處理器會主動地監控外部事件(比如快取失效),有些已經執行完的操作會因為這些事件而被撤銷,但不算“退休”。這就是說,x86知道自己的記憶體模型應該是什麼樣子的,當發生了一件和這個模型衝突的事,處理器會回退到上一個與記憶體模型相容的狀態。這就是我在以前另一篇文章中提到的“清除記憶體排序機(memory ordering machine clear)”。最後的結果是,x86處理器為記憶體操作提供了很強的一致性保證——雖然沒有達到完美的順序一致性。

無論如何,一篇文章講這麼多已經夠了。我把它放在我的部落格上。我的想法是將來的文章只要引用它就行了。我們看效果吧。感謝閱讀!

相關推薦

cache一致性入門解答

我計劃寫一些關於多核場景下資料組織的文章。寫了第一篇,但我很快意識到有大量的基礎知識我首先需要講一下。在本文中,我就嘗試闡述這些知識。 快取(Cache) 本文是關於CPU快取的快速入門。我假設你已經有了基本概念,但你可能不熟悉其中的一些細節。(如果

Android入門編譯錯誤匯總

art can 手動 already 提示 文件夾 som 原因 兩個 1 描寫敘述: 項目常常須要引用別人的libraryproject,在選項中add進來後,點擊應用或者確定。關閉頁面。 回到代碼中卻發現無法鏈接,又一次打開properties查看,發現導入的p

LintCode Python 入門題目 斐波納契數列

ima 算法 app mage 個數字 img ... 分享 spa 原題描述: 查找斐波納契數列中第 N 個數。 所謂的斐波納契數列是指: 前2個數是 0 和 1 。 第 i 個數是第 i-1 個數和第i-2 個數的和。 斐波納契數列的前10個數字是: 0, 1, 1

sysV init服務腳本(入門

chkconfig在sysV風格的init系統中,以Centos 6.x為例,系統服務腳本一般在/etc/rc.d/init.d目錄下,每個支持sysV init的腳本,一般都可以接受如下參數: start|stop|restart|status //這就是我們在centos下常用的控制服務啟動與停

全網首創ISE入門教程

加法 表示 cnblogs htm padding 九月 雲服務器 對他 實驗   轉眼間我已經大三了,現在成為了實驗室的負責人,對於下一屆學生的納新重任就交到了我的手上,想采取不同的方法暑假就可能對他們進行一些培訓,所以制作了此教程,說實話,在網上還沒有找到關於ISE的

主從DB與cache一致性

細節 href gem scene 邏輯 答案 導致 數據庫主從 blank 本文主要討論這麽幾個問題: (1)數據庫主從延時為何會導致緩存數據不一致 (2)優化思路與方案 一、需求緣起 上一篇《緩存架構設計細節二三事》中有一個小優化點,在只有主庫時,通過“串行化”的思

Unity黑巧克力 滾球遊戲 入門教程

教學 操作 遊戲 界面 版本 查看 入門級 get 入門 《黑巧克力》系列教程是適合於新手上手Unity的教程,本教程適合作為初次接觸Unity(零基礎)的第一篇的教程。學習本教程需要有的基礎是:線性代數、編程基礎、Csharp語言基礎、Unity自學經歷3天以上。如果讀者

入門貪心算法——java實現

種類 循環 決策 一個 pri target 下一個 元素 can 貪心算法入門 貪心算法是一種思路,而不是一種公式。 認真看,一會兒就會了! 個人網站:多貓影視(能看各大vip視頻)www.duomao.xyz package com.niu.test;

Android Studio 入門教程

努力 啟用 tel key map oid fonts class first 引用原文:http://www.cnblogs.com/abao0/p/6934023.html 寫博客是為了記住自己容易忘記的東西,另外也是對自己工作的總結,文章可以轉載,無需版權。希望盡自

JSTL <C:if></C:if> 和<C:ForEach></C:ForEach> 入門~

ava 條件 title spa b2c colspan geb 屬性 oar 一、<C:If>標簽:條件判斷語句 <c:if test="${objList.nodetype == 1}">上級節點</c:if>

java web 入門 開發 常用頁面調試方法

文件的 數據 生效 str debugger 操作數 ron 速查 現在 這裏介紹一下Java web 入門級開發中常用的代碼調式方法; ( 僅供入門級童靴 參考) ; 工具: chrome 瀏覽器 (版本越高越好); Java web 入門級開發 主要就是兩個方

Oracle-4 - :超級適合初學者的入門筆記:plsql,基本語法,記錄類型,循環,遊標,異常處理,存儲過程,存儲函數,觸發器

個人 就會 逗號 n) 循環結構 less 寫上 所有 targe 初學者可以從查詢到現在的pl/sql的內容都可以在我這裏的筆記中找到,希望能幫到大家,視頻資源在 資源, 我自己的全套筆記在 筆記 在pl/sql中可以繼續使用的sql關鍵字有:update delet

webService 入門

toc main方法 endpoint fun sdl java應用程序 成功 出現 eclipse 1,建立一個項目名為Trans,web項目,普通java項目都可以!這裏我們就以簡單的java應用程序來作為示範吧! 1.1在建立一個方法屬於com.shu.f

Cache一致性與DMA

緩存 重新 得到 失效 必須 index 數據 直接 寫入 cache一致性與DMA 第一個問題 對於進行DMA操作的設備, 並不是所有系統都保持它們的cache一致性。在這種情況下, 準備進行DMA的設備可能從RAM得到陳舊的數據, 因為臟的cache行可能還駐留在各個C

【58沈劍架構系列】主從DB與cache一致性

帶來 時序 增長 卡住 而是 一個 png bubuko 為什麽 本文主要討論這麽幾個問題: (1)數據庫主從延時為何會導致緩存數據不一致 (2)優化思路與方案 一、需求緣起 上一篇《緩存架構設計細節二三事》中有一個小優化點,在只有主庫時,通過“串

正則表達式(輕松入門

構造 day 過濾 文件 col 轉義符 pla value 簡單 前言:正則表達式又稱為火星文,因為視覺上讓人感覺很復雜。本文將會詳細介紹正則表達式,初學者只要認真閱讀過,定會有收獲。 1、正則的概念   正則表達式(regular expression)是一個描述字符規

JSON-B 精選課程,入門菜鳥必讀!

arch 出現 abstract ear 運行時 china topic oba java Java EE 雖然支持 XML 已久,但顯然遺漏了對 JSON 數據的內置支持。Java EE 8 的出現改變了這一狀況,給核心 Java 企業平臺帶來了強大的 JSON 綁定特性

PhpStorm中如何使用Xdebug工具,入門操作方法

sso nts 日誌文件 允許 deb src sdn 2.3 選擇 2.1準備工作 PHPSTORM版本 : 8.0.3 PHP版本 : 5.5.12 xdebug版本:php_xdebug-2.2.5-5.5-vc11.dll 註 : php版本和xdebug版本

Git 與 GitHub 入門

pam n) 暫存區 新版 是個 gin https ID and 今天我們來搞一下Git 這東西雖然沒啥搞頭兒,但是開發當中還必須得會用,誰讓你我都是苦逼的開發呢~~~~ 一、下載與安裝 這玩意簡單,給你賦個圖片,自己研究一下~~~~ 1.官網:https://git-s

Docker容器-入門

contain works nth s/4 attach 基礎設施 發展 網絡命名空間 pts 1.1 容器簡介 1.1.1 什麽是 Linux 容器 Linux容器是與系統其他部分隔離開的一系列進程,從另一個鏡像運行,並由該鏡像提供支持進程所需的全部文件。容器提供的鏡