《深入理解計算機系統》筆記(四)虛擬存儲器,malloc,垃圾回收

轉:https://blog.csdn.net/shijunwang/article/details/80217266

歡迎查看《深入理解計算機系統》系列博客

《深入理解計算機系統》筆記(一)棧

《深入理解計算機系統》筆記(二)內存和高速緩存的原理

《深入理解計算機系統》筆記(三)鏈接知識

《深入理解計算機系統》筆記(四)虛擬存儲器,malloc,垃圾回收(本篇)

《深入理解計算機系統》筆記(五)併發、多進程和多線程【Final】

——————————————————————————————————————–

概述

        我們電腦上運行的程序都是使用虛擬存儲,跟物理內存根本不搭邊

        ●既然虛擬內存是在磁盤上的,爲什麼它又運行這麼好,並沒有感覺卡頓?這要感謝程序的局部性!

        ●虛擬存儲器的調度是一個操作系統必須做好的事情!不然程序就會慢的跟蝸牛一樣。

        ●垃圾收集器就是一個動態分配器,它自動釋放這些程序不再需要的已分配的塊。

        ●在64處理器的PC上獲取一個變量的地址發現是:6個字節?比如i7的核64位,但是隻能尋址48位,所以指針是6個字節。指針字節跟操作系統的位沒有必然關係。

        ●由於虛擬存儲器的存在使得程序鏈接和加載都很容易。比如程序的“代碼段”,總是從虛擬地址0x08048000開始的。

        ●“寫時拷貝”是一個非常重要的概念。

        一個系統中的進程是與其他進程共享CPU和內存資源的,然而,如果太多的進程需要太多的存儲器,那麼他們中的一些就根本無法運行。存儲器還很容易破壞。如果某個進程不小心寫了另外一個進程的存儲器,它就可能易某種完全跟程序邏輯無關的方式失敗。爲了更加有效地管理存儲器並且少出錯,現代系統提供了一種對內存的抽象概念,叫做虛擬存儲器(VM).

        作爲程序員我們爲什麼要了解虛擬存儲器呢。1)虛擬存儲器是中心。虛擬存儲編輯系統的所有層面。2),虛擬存儲器是強大的。虛擬存儲器給予應用程序強大的能力,可以創建和銷燬存儲器片(chunk)、將存儲器片映射到磁盤文件的某個部分,以及其他進程共享存儲器。3),虛擬存儲器是危險的。每次應用程序引用一個變量、間接引用一個指針,或者調用一個類似與malloc的動態分配的函數,就會跟虛擬存儲器打交道。

9.1物理和虛擬尋址

    早期的計算機採用物理尋址(Physical Addressing),現代的計算機採用虛擬尋址(virtual addressing),將一個虛擬地址轉換爲物理地址的任務叫做地址翻譯。地址翻譯需要CPU硬件和操作系統之間的緊密合作。CPU芯片中有一個MMU的存儲器管理單元(Memory Management Unit)。如下圖所示:



從上面兩張圖上發現,虛擬尋址只是CPU的一種技術。

9.2地址空間

地址空間是一個非負數地址的有序集合:如果地址空間的整數是連續的,那麼我們說它是一個線性地址空間。那麼一個擁有虛擬尋址的CPU,也就會有一個虛擬地址空間。內存中的一個字節,就會有一個物理地址和一個虛擬地址。如果CPU有N個虛擬地址空間,那麼該字節就有N個虛擬地址。

9.3虛擬存儲器作爲緩存的工具

概念上而言,虛擬存儲器被組織爲一個由“存放在磁盤上的N個連續字節大小的單元”組成的數組。磁盤上的內容被緩存到內存中,內存和磁盤之間採用塊block格式傳輸。由於虛擬存儲的技術早於高速緩存的技術,所以當時這種塊被稱之爲頁(page),也就是現在的說法。如下圖:


上圖展示了一個8個虛擬頁的小型虛擬存儲器。綠色的3個頁表示已經緩存到內存了(參考內存中綠色的三個頁)連個未分配和三個未緩存的。

    由於內存的訪問速度比磁盤快100000倍,所以如果內存不命中,那麼代價將是非常高的(程序運行將非常慢),另外,讀取磁盤某一個扇區第一個字節的開銷比起讀取整個扇區的字節慢100000倍。這兩個條件決定了虛擬存儲的頁的大小最好是一個扇區或幾個扇區。典型地是4KB-2MB。

    還有,即便是有個很大的頁,如果操作系統在替換頁的操作處理不當,速度將會很慢。缺頁是一種異常,名字就叫“缺頁”,發生異常後,虛擬存儲器加載磁盤到內存並替換犧牲頁。操作系統再次運行該指令,就不再缺頁了。

    當我們與多人瞭解了虛擬存儲器的概念之後,我們的第一印象是:“它的效率應該很低下吧,因爲如果不命中,那麼代價將非常高。”我們也擔心頁面的調度破壞程序的性能。實際上,虛擬存儲器運行的相當好,主要歸功於程序局部性(locality).

    儘管整個運行過程中程序引用的不同頁面的總數可能超出物理存儲器的總的大小,但是局部性原則保證了在任意時刻,程序將往往在一個較小的活動頁面(active page)集合工作,這個集合叫做工作集。

    tips 在Unix系統中使用getrusage函數來檢測缺頁的數量

9.4虛擬存儲器作爲存儲器管理工具

   實際上,操作系統爲“每一個進程”提供了一個獨立的頁表,因而也就是一個獨立的虛擬地址空間。展現形式就是:讓人感覺這個程序正在完全是使用CPU和內存資源。如下圖,多個虛擬葉面同事映射到同一個共享物理頁面上(動態鏈接庫或者叫共享庫就是這個原理)。


    進程獨立的地址空間允許每個進程的存儲器映像使用相同的基本格式,而不管代碼和數據實際存放在物理存儲器的地方。比如Linux系統上每個進程文本節總是從虛擬地址0x08048000初開始。總結起來說:虛擬存儲器簡化了鏈接、簡化了加載、簡化共享和簡化存儲器分配。

9.5虛擬存儲器作爲存儲器保護的工具

    現代操作系統都嚴格限制普通應用程序訪問自己的只讀數據和別進程的數據,還有操作系統的內核部分。所以虛擬存儲器可以在頁的開始部分設置幾個標誌位,用於標明這個進程是系統進程還是用戶進程。如果某一天指令違反了這個條件,那麼CPU就觸發一個異常。Unix外殼一般將這種異常報告爲“段錯誤segmentation fault”

9.6地址翻譯,有點兒專業。跟應用程序層面關係不大。

9.7案例研究:Intel Core i7/Linux存儲器系統

    在64位的mac電腦上打印一個變量的指針,結果指針是6個字節。這是因爲i7的CPU只支持48位的虛擬地址,和52位的物理地址空間。雖然我的mac是i5的但是估計也是48位虛擬地址。其他內容是地址翻譯,缺頁異常。

    Linux虛擬存儲器區域:Linux將虛擬存儲器組織成一些區域的集合。一個區域(area)就是已經存在着(已分配的)的虛擬存儲器的連續片(chunk)

9.8存儲器映射

    有一個疑問:“電腦4G的內存,加載一個程序很慢,但是看看內存剩餘量,還有剩餘內存,不應該慢啊。如果升級到8G的內存,程序加載的快了。這是因爲操作系統有一個叫做交換空間的東西,交換空間可能顯示虛擬存儲器的頁數“。明白了,如果內存大,交換空間就大,程序加載就快。

    概念:私有有的寫時拷貝,理解這個概念很重要。用圖來說明:


    上圖右部分說明了寫時拷貝:進程1和2都共享內存中的一組頁,但是進程2需要寫最後一個頁。那麼啓動寫時拷貝,拷貝最後一頁到內存其他地方。(內存中的頁,可能不是連續的)。

    fork函數直接在虛擬存儲器上開闢一段,跟主進程一不一樣的拷貝,那麼,也就跟主進程指向同一個物理存儲器。從上圖左邊部分中我們可以印證一下進程2可以看做是從進程1調用fork函數創建的。

    execve是如何加載應用程序的。execve函數在當前進程中加載並運行a.out,用a.out程序有效的替換了當前程序。加載a.out時候需要以下幾個步驟:

    1)刪除已存在的用戶區域。

    2)映射私有區域。爲新的程序的文本、數據、bss、和棧區域創建新的區域結構。

    3)映射共享區域。

    4)設置程序技術器PC。使之指向文本區域的入口點。

9.9動態存儲器分配malloc

    動態分配,我自己的理解就是分配在虛擬存儲器中的區域,這個區域可能已經在內存中了,也可能在磁盤上。

    下面是真正的定義:動態存儲器分配器,維護着一個進程的虛擬存儲器區域,成爲”堆“,對於每一個進程,操作系統內核維護着一個變量brk(break)指向堆的頂部。分配器有兩種基本風格。

    1)顯式分配器:例如C和C++的malloc 和new 運算法。但是需要程序員收到free和delete處理。

    2)隱式分配器:也叫做垃圾收集器,其自動釋放未使用的已分配的塊的過程叫做垃圾收集。例如:Java的垃圾回收機制。

    動態存儲分配器的要求和目標

    ●處理任意請求序列。

    ●立即響應請求。分配器必須立即響應分配請求。因此不允許分配器提高性能,從新排列或者緩衝請求。

    ●只是用堆。

    ●對齊塊,比如8個字節的對齊。

    ●不修改已分配的塊。不能壓縮已分配的塊。

    目標

    ●1最大化吞吐率

    ●2最大化存儲器的利用率。天真的程序員經常不正確的假設虛擬存儲器是一個無限的資源,事實上,一個系統中被所有進程分配的虛擬存儲器的全部數量是受磁盤上交換空間的數量限制的。

    存儲器碎片(這是一個很有意思的話題)。首先介紹內部碎片和外部碎片。如圖:

    

    外部碎片要比內部碎片處理複雜得多!分配器還要有其他功能:合併空閒的塊,也是非常有意思的(原書使用19頁的量,來描述分配器的原理和動作),聰明的程序員採用了很多種技巧來實現,參考p568頁。

9.10 垃圾收集(回收)

    垃圾收集器就是一個動態分配器,它自動釋放這些程序不再需要的已分配的塊。垃圾收集可以追溯到20世紀60年代早起在MIT開發的Lisp系統。他是諸如Java、ML、Perl和Mathematica等現代語言系統的一個重要的部分。
    垃圾回收的原理,如圖。那些灰色的小圓點表示已經是垃圾了,需要清理。

    是用專業的詞語是:上圖是一張可達圖,藍色的塊表示可達,灰色的塊表示不可達(從任意根節點出發不可達)。

9.11程序中常見的與存儲器有關的錯誤。

    ●間接引用壞指針

    ●讀未初始化的存儲器

    ●允許棧緩存去溢出

    ●假設指針和它們指向的對象是相同大小的

    ●造成錯位

    ●引用指針,而不是它指向的對象

    ●誤解指針運算

    ●應用不存在的變量

    ●應用空閒塊中的數據

    ●引起存儲器泄露

總結

虛擬存儲器的三個重要功能:
第一,它在內存中自動緩存“最近使用的存放在磁盤上的”虛擬地址空間的內容。
第二,虛擬存儲器簡化了存儲器的管理,簡化鏈接和共享
第三,虛擬存儲器通過在每條頁表條目中加入保護位從而簡化了存儲器保護。
        </div>
            </div>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章