1. 程式人生 > >虛擬儲存器(1)——虛存概念及頁、頁表和地址翻譯基礎

虛擬儲存器(1)——虛存概念及頁、頁表和地址翻譯基礎

一、前言

        虛擬儲存器,感覺很難,至少說很複雜,裡面涉及到的東西也比較枯燥。當然,如果能徹底搞清楚,對繼續學習作業系統原理是百利無一害的。

        玩C或C++的人,經常通過&a的方式獲取變數地址,並將其賦值給指標變數,也通常用printf打印出地址的值,類似0x8048

034之類的地址值,但要從此刻開始要明確一點,你打印出的這個地址值,根本不是記憶體裡的真實值,而是通過連結器最終生成的虛擬地址,這個虛擬地址幫助程式在作業系統程序中正常執行,而每個程序裡的虛擬儲存分佈的值幾乎都一樣……所以才會出現,你各程序中打印出的地址為啥分佈區間那麼相似。

        虛擬儲存器是硬體異常、硬體地址翻譯、主存、磁碟檔案和核心軟體的完美互動,它為每個程序提供了一個大的、一致的、私有的地址空間。它有三個重要的功能:

        ①它將實體記憶體看成磁碟的快取記憶體,永遠只保留活動區域,從而高效利用實體記憶體

        ②它為每個程序提供一致的線性地址空間,從而簡化儲存器管理

        ③它保護每個程序的地址空間不被其他程序破壞。

        真實實體記憶體中,空閒空間並非連續,可能零散的分佈在各個角落,而虛擬儲存器,就是把各物件的地址值分佈在連續線性的邏輯地址空間中。那麼如何將線性地址和零散的實體地址對應起來呢?這就需要地址翻譯。CPU在處理物件地址時,利用內部MMU,通過訪問存放在實體記憶體中的查詢表來動態翻譯虛擬地址,該表內容是由作業系統管理的。

        扯了這麼多是不是覺得很亂? 這裡涉及到一個重要的儲存器層次結構的概念。回憶下我寫的《程式效能優化探討(3)》章中的第一節就講到“儲存器層次結構”。裡面圖清晰的把儲存器層次結構描述了出來:儲存速度由快到慢、由高到低一次是:CPU暫存器、L1\L2\L3快取記憶體(SRAM)、主存(記憶體,DRAM)、磁碟……

        當世界上還有沒有虛擬儲存器概念時,記憶體DRAM顯然就是磁碟的上一級快取,就像SRAM是記憶體DRAM的上一級快取一樣類似。but現在發展出了虛擬儲存器的概念,在磁碟上開闢了連續的空間玩票,那麼我們可以理解為:虛擬儲存器VM事實上成為磁碟的快取!而誰又是VM的上一級快取呢?自然而然就應該想到是記憶體DRAM。事實上VM相當於從儲存器體系中的記憶體和磁碟之間的位置強行插入,新增的一個層次罷了!為啥要強插?很簡單。因為磁碟讀取速度實在太慢,極端情況下可能比DRAM的速度慢10 0000倍,根據儲存器層次結構的思想,為了調和速度差,增加一個叫做虛擬儲存器的快取結構也就理所當然了。

二、虛擬儲存器一般性概念

        虛擬儲存器成簡稱為VM。

        先來看下比較拗口概念解釋:虛擬儲存器被組織為一個由存放在磁碟上的N個連續的位元組大小的單元組成的陣列……這句話除了覺得很抽象外,我們至少能獲得兩個關鍵資訊:

        ①虛擬儲存器的載體是磁碟,他和其他儲存器一樣,存放資料,而且它在磁碟上是連續儲存的;

        ②虛擬儲存器的每個位元組資料一定對應唯一的1bit虛擬地址。

        首先,我們要分清楚虛擬儲存器和虛擬地址的區別,一個是邏輯上儲存資料的物件;一個是邏輯上標識這些資料的概念地址。根據以上的定義,我們可以YY一下虛擬儲存器的大概應用:虛擬儲存器似乎和物理儲存器有某種強對應關係,它利用磁碟上連續的儲存資料,可以實現線性連續地址空間訪問。也許他能優化磁碟與實體記憶體器之間的通訊關係。

        接下來我們先惡補看下什麼是線性地址空間。回憶下,比如你有n位(bit)的,資料位,你能表達最大的整數就是2^(n) - 1,你能表示的所有整數的個數就是N=2^(n),(注意哦,最大整數是從0起的,而數個數是從1起:)),也就是說,能表示的所有數為:(0,1,2,…,N-1)這N個數。如果每個數都來標識一個地址,而每個地址又代表8位元組(byte)的大小的空間,那麼N個整數就能標識N*8位元組的線性地址空間。

        舉個例子,32位機,由於是32bit,而每個位元組又是8bit,所以32位機就是由4byte組成地址值。比如0x12345678,就是典型的4位元組,32位。注意這裡的位(bit)可不是16進位制0x裡表示的某一個位哦!而是比如0x12,表示一個位元組,對應8位,它的二進位制就是00010010。

        32位系統的n=32,那麼N=2^(2+10+10+10)=4Gbit,它的線性地址空間就是(0,1,2,...,4G-1)。這裡再廢話一下,為啥說32位機可以標識最大4Gbyte的地址,而這裡算出來最大是4Gbit?哼哼!傻瓜才去用1bit地址對應1bit資料,那樣多浪費啊!肯定是1bit地址對應8bit的資料,也就是說一位地址對應一位元組資料,這樣才合理嘛!!!

         我們設n和N表示虛擬地址空間,m和M表示實際實體地址空間。有了前面的鋪墊,我們可以想象一下,一位元組資料,比一定只對應1bit地址哦!可以有多個1bit地址號稱標識這一位元組資料哦!至少目前,我們有N和M兩個地址空間,可以同時來標識某一位元組的資料!

        接下來描述一個事實:磁碟上的資料要想快取到實體記憶體中,不可能一位一位快取進記憶體,也不可能一位元組一位元組快取進記憶體。那到底以多大的批量快取呢?我們把這個批量稱為塊,磁碟上的資料被分割為塊,將它作為磁碟與記憶體之間的傳輸單元。好了,虛擬記憶體如果要來湊熱鬧,也得有相應的機制,於是VM系統也將VM分割成固定大小的塊,我們將它稱為虛擬頁(VP)。這虛擬頁既然有某個固定大小,肯定是多少位元組之類的單位,於是我們定義虛擬頁大小為P=2^p位元組。當然,實體記憶體肯定也有類似的頁大小。

        卡住!是不是又有點暈了?P=2^p位元組是個什麼東東?這裡我們可以藉助極限思維來想象一下:如果壓根沒有虛擬頁這種東西,那麼我們傳資料通常一位元組一位元組來玩,於是此時p=0,P=1位元組,對否?哦,也就是說,這裡的p,很類似N=2^n,利用位的概念來標識虛擬頁的位元組大小。假設VM系統的原始碼裡要定義虛擬頁大小,應該是類似unsigned char page_addr_len之類的變數,就可以標識虛擬頁P大小是256 = 2^8。

        好了,我們知道由於虛擬頁的存在,將連續的虛擬儲存空間分割成若干個虛擬頁,當虛擬頁大小是P位元組,而我們又曉得整個虛擬空間大小是N位元組,於是能輕易得出,虛擬儲存器被分割成N/P=2^(n-p)個頁,也就是我們得到虛擬頁空間(0,1,2,……,N/P-1)。

        同理,實體記憶體也有類似的分頁概念,也被分割成M/P=2^(m-p)個頁,於是得出物理頁空間(0,1,2,……,M/P-1)。

三、虛擬頁和頁表

        好了,有了這麼冗長的鋪墊,再來看下面的圖,應該沒那麼懵了吧:

        左邊的VP代表虛擬頁面,右邊的PP代表物理頁面,我們可以看到,虛擬儲存器中的頁原封不動的對應上了物理儲存器中的頁。

        虛擬頁面的集合都分為三個不相交的子集:

        未分配的:說明該頁沒對應任何資料,因此就不佔用磁碟空間……哇塞,大家有沒有發現——不佔用磁碟空間!難道說,VM雖然在磁碟上申請了連續空間,但它具體使用時不一定按連續的方式佔用空間?有待考證;

        快取的:當前快取在物理儲存器中的已分配頁……首先它得是已分配的頁(有資料和它關聯),並且當前正臨時放在物理儲存器中玩的頁;

        未快取的:沒有快取在物理儲存器中已分配的頁……首先它也是已分配的頁(有資料和它關聯),只是暫時被剔除出物理儲存器了。

        神馬?有人問為啥圖的右邊比左邊短?→_→介個……我想說的是,兩邊在某個極端情況下可能一樣高,此時M=N,這是什麼時候?當你玩32位機時,土豪般的插了4G的記憶體條, 有可能兩邊一樣長O(∩_∩)O~一般來說,屌絲插的記憶體條肯定少於理論最大記憶體值的,而虛擬儲存器是可以設定成理論最大值,不信你到windows設定中找到虛擬記憶體設定,我的32機允許設定的最大值就是4092MB,因此作者畫這個圖時也充分考慮了這種普遍性……作者真為屌絲考慮O(∩_∩)O~

        既然DRAM快取的虛擬頁面,那VM系統總需要一個種方法來判定某個虛擬頁是否存放在DRAM中,如果是,系統還必須確定這個虛擬頁存放在(DRAM)中哪個物理頁中;如果不是(不命中),VM系統還必須尋找這個虛擬頁在磁碟的什麼位置。這就有兩種情況:

        ①該虛擬頁已經被VM系統分配,只是該虛擬頁還未被快取到DRAM中——說明該虛擬頁已經對應上了磁盤裡具體的某塊資料

        ②該虛擬頁還未被VM系統分配,也就是說這個虛擬頁編號在VM看來還不存在——說明在磁碟上的虛擬儲存器中找不到該虛擬頁號(這種強行訪問可能會出段錯誤哦!!!)

        問題就是,誰來標識虛擬頁這些不同的狀態呢?答案是頁表!首先注意,頁表是專門存放在記憶體DRAM中特定區域的,它全面記錄了所有可能的虛擬頁號的當前情況:

        從上圖中可以看出,右上方的表,描述了記憶體DRAM中PP0~PP3Z這4個物理頁中存放的VP1~VP4這四個虛擬頁表;右下方的表,描述了虛擬儲存器中已分配的虛擬頁號;

        而左邊的表,就是大名鼎鼎的頁表。PTE是頁表條目,有效位為1時說明該虛擬頁快取在DRAM中,否則便有是上面描述過的兩種情況:

        ①VP3和VP6兩個虛擬頁已經被分配,只是暫未快取到DRAM中;

        ②VP0和VP5兩個虛擬頁還未被分配,於是對應的PTE0和PTE5就是null——說明這兩個頁號還沒對映磁碟上的其他資料(比如暫時沒需求),因此頁表自然也將其置空了。

        好了,這麼完美動人的頁表,是誰來生成和維護呢?答案是作業系統,而且作業系統還要負責完成磁碟和DRAM之間具體的傳送頁工作。

        那又是誰會需要去讀取頁表的內容呢?是CPU裡MMU(儲存器管理單元)中的地址翻譯硬體,當CPU需要將一個虛擬地址轉換成實體地址時,都會讀取頁表的內容。

        說到這我們稍微總結一下:虛擬儲存器部署在磁碟上;頁表部署在記憶體中;OS維護頁表內容;MMU讀取頁表內容。

        一旦理解了頁表,類似頁命中和缺頁就好理解了。比如上圖中你引用VP1\2\7\4都是頁命中,可你要想起引用下VP3,地址翻譯硬體讀取PTE3時發現有效位0,於是處罰缺頁異常。

        缺頁也沒什麼大不了,此時核心會呼叫異常處理程式,通過某種策略決定要趕走PP3中的VP4,此時無論VP4是否被修改,核心都會將其寫回磁碟,然後從磁碟拷貝VP3到儲存器PP3中,並更新PTE3,隨後返回並重啟導致缺頁的指令,地址翻譯硬體會重新翻譯VP3的虛擬地址到實體地址,如下圖:

       

         這裡專門提一下,以上討論都針對的虛擬儲存器——頁表——物理儲存器之間的資料交換,那麼虛擬儲存器和磁碟上原始資料的關係如何呢?是否清楚?事實上,每個程序都有自己的虛擬儲存器,相當於每個程序都在磁碟上開闢了屬於自己的虛擬頁空間,因此每個程序都有諸如VP1~VP7這些虛擬頁儲存在磁碟上,因此物理儲存器中也為每個程序準備了獨立的頁表。但是,物理儲存器卻可能被不同程序的虛擬頁共享到同一個物理頁上!這有什麼好處呢,比如標準庫中的printf函式,每個程序都可以將這個相應的虛擬頁對映到同一個物理頁上。

        還有一個更神奇的細節,既然磁碟上的可執行檔案在變成程序時,又會在磁碟上的虛擬儲存器中開闢屬於自己的私有虛擬頁區域,那麼可執行檔案的文字部分(.init、.text、rodata等)就會同時出現在磁碟的兩個位置?!也就是說,我們所謂的linux程序的儲存器對映(如下圖),根本就不放在記憶體中,而是放在磁碟中?!是不是很毀三觀?事實上,載入器從來不把任何可執行檔案的資料拷貝到物理儲存器中,而是在頁面第一次被引用時,由虛擬儲存器系統從磁碟調入物理儲存器的!也就是說,下圖這個偉大的程序對映的實體,事實上是放在磁碟中的虛擬儲存器裡,並且被VM系統分配成諸如VP1~VP7這些虛擬頁面,然後按需依次調入物理儲存器的!

       

四、地址翻譯

地址翻譯聽起來很高大上,但如果看明白上面的講解,尤其是理解了虛擬頁物理頁以及頁表的概念,那理解起地址翻譯來也是灰常容易的!

       回憶下,既然虛擬儲存器被分配成一個個虛擬頁,每個虛擬頁大小的是2^p位元組,那肯定有一個方法去識別,某個位元組或者某段資料具體屬於哪個虛擬頁,又在這個頁的哪個位置,對否?既然虛擬頁大小是2^p位元組,說明我們用p位二進位制地址就能標識這個頁中的每一個位元組了。而虛擬地址共n位,也就說還剩n-p位,幹嘛呢?當然是標識虛擬頁號了,有n-p位就能標識2^(n-p)個虛擬頁。(注意哦,2^p的單位是位元組,2^(n-p)的單位是“個”千萬別搞混!

        我們把標識2^p位元組虛擬頁中具體位元組位置的量稱為虛擬頁面偏移量,用VPO表示;把標識2^(n-p)個虛擬頁的號稱為虛擬頁號,用VPN表示。於是就有下面的虛擬地址結構:

VPN     (n-p)位 VPO    p位
        而物理頁也有相同的結構,其實體地址結構:
PPN     (n-p)位 PPO    p位

        再回憶下,頁表這個東西,為什麼能把虛擬頁和物理頁對應聯絡起來呢?事實上,頁表條目的PTE陣列才是對應關係的關鍵,PTE陣列下標能對應相應的虛擬頁號VPN,而陣列中存的又是物理頁號PPN,這樣當VPN與PPN建立強聯絡後,剩下的偏移位,直接將VPO複製給PPO就完事,於是有了下面的過程圖:



        這裡又會涉及到一個現象,既然實體記憶體有上一級快取SRAM,比如說就是L1,CPU中MMU與實體記憶體之間的任何資料交換都會經過L1的查詢和處理。比如,MMU需要和實體記憶體交換PTE、PTEA(PTE的標號)、PA(實體地址)這些資訊,L1都會檢查是否先前已有快取。如果命中,直接由L1提供MMU所需的資料;如果不命中,則由L1向實體記憶體申請資料,於是就有了下圖的過程:

        

        那麼MMU在訪問L1時,是用實體地址還是虛擬地址呢?相信自己腦補都應該有結論,肯定是實體地址啦!至少大部分系統都是這麼處理的。

五、利用TLB加速地址翻譯

        從目前的設計來看,MMU如果需要翻譯CPU產生的虛擬地址,就必須獲得PTE,恰巧L1命中的情況還好,若不命中,就得花費幾十上百的週期到實體記憶體中呼叫。而貪婪的人類試圖儘可能的消除這樣的開銷,於是在MMU中專門開闢了一個小的緩衝區,稱為翻譯後備緩衝器TLB(Translation Lookaside Buffer)。

        事實上,TLB很類似對虛擬地址已有的劃分策略,比如之前我們把虛擬地址劃分為VPN和VPO,都是為了體現特定的層次結構。那麼現在可以把VPN部分再進行劃分:


        TLB是由標記(TLBT)和索引(TLBI)組成,如果TLB有T=2^t個組,那麼TLBI就剛好是t位,而VPN剩餘位就剛好留給TLBT……留來幹什麼?其實我也還搞不清楚,待下節分解。

相關推薦

虛擬儲存器1——概念地址翻譯基礎

一、前言         虛擬儲存器,感覺很難,至少說很複雜,裡面涉及到的東西也比較枯燥。當然,如果能徹底搞清楚,對繼續學習作業系統原理是百利無一害的。         玩C或C++的人,經常通過&a的方式獲取變數地址,並將其賦值給指標變數,也通常用printf打

【深入Java虛擬1】:Java內區域與內溢出

count 遇到 leak 分析 對象類型 深度 分配內存 解釋執行 尋址 內存區域 Java虛擬機在執行Java程序的過程中會把他所管理的內存劃分為若幹個不同的數據區域。Java虛擬機規範將JVM所管理的內存分為以下幾個運行時數據區:程序計數器、Java虛擬機棧、本地方法

leveldb 閱讀筆記1分配

erp ant 保存 系統調用 tin assert blog 方便 png 內存管理對於任何程序都是很重要的一塊,leveldb自己也實現了一個簡單了內存分配器,而不是使用一些其他開源軟件tcmalloc等,避免了對其他軟件的依賴。 自己實現內存分配器有什麽好處呢?

繼承派生1:繼承概念繼承方式

派生類的定義: 看一個有趣的例子: #include <iostream> using namespace std; class A { public: void f(int i){cout<<i<<endl;}

第五章 虛擬儲存器 *

※抖動 系統抖動: 為了提高處理機利用率,可增加多道程式併發度; 但程序數目增加過多,每個程序分配得到的物理塊太少,在某個臨界點上,會出現剛被淘汰的頁很快又需重新調入;而調入不久又被淘汰出去;出現頻繁缺頁 大部分處理器時間都用在來回的頁面排程上,這

類繼承-虛擬函式1

關於基類的引用和指標與派生類的指標和引用: 基類的引用可以初始化為派生類的物件,可以通過基類引用呼叫派生了新增的公有方法,也可以呼叫基類的方法,指標也是一樣。反之,將派生了的引用初始化為基類物件是不允許的。 Based based = Based(2); Derived derive

Nginx 教程 1:基本概念

簡介 嗨!分享就是關心!所以,我們願意再跟你分享一點點知識。我們準備了這個劃分為三節的《Nginx教程》。如果你對 Nginx 已經有所瞭解,或者你希望瞭解更多,這個教程將會對你非常有幫助。 我們會告訴你 Nginx 是如何工作的,其背後的概念有哪些,以及如何優化

Kubernetes筆記1:基本概念

有關Kubernetes是什麼,網上很常見的一種介紹是:Kubernetes是Google開源的容器叢集管理系統,其提供應用部署、維護、擴充套件機制等功能,利用Kubernetes能方便地管理跨機器執行容器化的應用。 由此可見,K8s是構建在容器(Docker

2.4.16版本Apache配置虛擬目錄1

看了韓順平配置Apache虛擬目錄的視訊教程,結果我照著裡面的說法做了一下,在httpd.conf檔案做如下處理: 1.把DocumentRoot  那一行註釋掉,我的註釋內容如下: 2.加入如下程

深入理解java虛擬1

方法區 對象實例 nat 物理 深入 ret 字符 java 所有 java內存區域 Java虛擬機執行java程序時會將管理的內存劃分為若幹個區域:   1. 程序計數器     程序計數器是一個”線程私有“的內存區域,用於獲取下一條需要執行的字節碼指令,如分支、循

論文: Data-Driven Evolutionary Optimization: An Overview and case studies1 資料驅動概念,文章結構,大數分類

宣告: 只作為自己閱讀論文的相關筆記記錄,理解有誤的地方還望指正  論文下載連結: 概念:資料驅動? Solving evolutionary optimization problems driven by data collected in simulation

【深入Java虛擬機器1】:Java記憶體區域與記憶體溢位

記憶體區域 Java虛擬機器在執行Java程式的過程中會把他所管理的記憶體劃分為若干個不同的資料區域。Java虛擬機器規範將JVM所管理的記憶體分為以下幾個執行時資料區:程式計數器、Java虛擬機器棧、本地方法棧、Java堆、方法區。下面詳細闡述各資料區所儲存的

activemq系列1-activemq相關概念

JMS: 即Java Message Service,是一種面向訊息的中介軟體(MOM:Message Oriented Middleware)。大致的過程是這樣的:傳送者把訊息傳送給訊息伺服器,訊息伺服器將訊息存放在若干佇列/主題中,在合適的時候,訊息伺服器會將訊息轉發給

《Javascript權威指南》學習筆記之十七:BOM新成就1--client儲數據Storage實現

globals 機制 ng- url new onclick views watermark -c 版權聲明:本文為博主原創文章。未經博主同意不得轉載。 https://blog.csdn

Linux C編程之十九1 libevent基本概念

通信 == socket通信 驅動 1.4 event http linu make 一、libevent是幹什麽的 1. 開源的庫, 提高開發效率 封裝了socket通信 封裝了IO多路轉接 2. 精簡, 專註於網絡, 性能高 3

大話效能測試系列1- 效能測試概念與主要指標

如果你對效能測試感興趣,但是又不熟悉理論知識,可以看下面的系列文章 https://www.cnblogs.com/poloyy/category/1620792.html   學習前的認知 我們在學習效能測試之前,需要有個新的認識:效能測試,不再是像功能測試一樣單純的找 Bug,而是去找效能指標

【開源】OSharp框架學習系列1:總體設計系列導航

正是 html 組織 內聚性 權限 是什麽 enc 3-0 分發 OSharp是什麽?   OSharp是個快速開發框架,但不是一個大而全的包羅萬象的框架,嚴格的說,OSharp中什麽都沒有實現。與其他大而全的框架最大的不同點,就是OSharp只做抽象封裝,不做實現。依賴註

GitHub使用教程——1使用GitHub創建簡歷展示

git github"如果我比別人看得遠,那是因為我站在巨人的肩膀上"這是加利利的第一篇文章系統環境:CentOS6.6桌面版工具:VNC及Xshell流程: 創建倉庫位置——初始化倉庫——配置用戶名及郵箱——下載簡歷模板——編輯簡歷——GitHub上創建倉庫——上傳本地代碼到遠程倉庫——使用托管源

spark學習1--ubuntu14.04集群搭建配置jdk

RM int 5.0 java_home 輸入 str cas Go 比較 環境:ubuntu14.04 1、文本模式桌面模式切換 ctrl+alt+F6 切換到文本模式 ctrl + alt +F7 /輸入命令startx切換到桌面模式 2、更改Ip地址、主機名 /

MySQL常用操作1設置更改root密碼連接MySQLMySQL常用命令

mysql常用操作 設置更改root密碼設置mysql的root用戶密碼:(默認為空)1.查看mysql任務是否開啟:ps aux |grep mysql若無開啟則-->/etc/init.d/mysqld start2.登錄mysql : /usr/local/mysql/bin