'內存不夠這麼辦?從程序的底層來分析'

"

在早期的計算機中,程序是直接運行在物理內存上的,也就是說:程序在運行時訪問的地址就是物理地址。這樣也就是單運行的時候沒有什麼問題!可是,計算機會有多到程序、分時系統和多任務,當我們能夠同時運行多個程序時,CPU的利用率將會比較高。那麼有一個非常嚴重的問題:如何將計算機的有限的物理內存分配給多個程序使用

假設我們計算有128MB內存,程序A需要10MB,程序B需要100MB,程序C需要20MB。如果我們需要同時運行程序A和B,那麼比較直接的做法是將內存的前10MB分配給程序A,10MB~110MB分配給B。

"

在早期的計算機中,程序是直接運行在物理內存上的,也就是說:程序在運行時訪問的地址就是物理地址。這樣也就是單運行的時候沒有什麼問題!可是,計算機會有多到程序、分時系統和多任務,當我們能夠同時運行多個程序時,CPU的利用率將會比較高。那麼有一個非常嚴重的問題:如何將計算機的有限的物理內存分配給多個程序使用

假設我們計算有128MB內存,程序A需要10MB,程序B需要100MB,程序C需要20MB。如果我們需要同時運行程序A和B,那麼比較直接的做法是將內存的前10MB分配給程序A,10MB~110MB分配給B。

內存不夠這麼辦?從程序的底層來分析

但這樣做,會造成以下問題:

  • 當多個程序需要運行時,必須保證這些程序用到的內存總量要小於計算機實際的物理內存的大小。
  • 進程地址空間不隔離,由於程序是直接訪問物理內存的,所以每一個進程都可以修改其他進程的內存數據,設置修改內核地址空間中的數據,所以有些惡意程序可以隨意修改別的進程,就會造成一些破壞
  • 內存使用效率低 內存空間不足,就需要將其他程序展示拷貝到硬盤當中,然後將新的程序裝入內存。然而由於大量的數據裝入裝出,內存的使用效率會非常低
  • 程序運行的地址不確定;因為內存地址是隨機分配的,所以程序運行的地址也是不正確的

解決這幾個問題的思路就是使用我們非常牛逼的方法:增加中間層 - 即使用一種間接的地址訪問方式。

把程序給出的地址看做是一種虛擬地址,然後通過某種映射,將這個虛擬地址轉化到實際的物理地址。這樣,只需要控制好映射過程,就能保證程序所能訪問的物理內存區域跟別的程序不重疊,達到空間隔離的效果。

隔離

普通的程序它只需要一個簡單的執行環境,一個單一的地址空間,有自己的CPU。

地址空間比較抽象,如果把它想象成一個數組,每一個數組是一字節,數組大小就是地址空間的長度,那麼32位的地址空間大小就是2^32=4294967296字節,即4G,地址空間有效位是0x00000000~0xFFFFFFFF。

地址空間分為兩種:

  • 物理空間:就是物理內存。32位的機器,地址線就有32條,物理空間4G,但如果只裝有512M的內存,那麼實際有效的空間地址就是0x00000000~0x1FFFFFFF,其他部分都是無效的。
  • 虛擬空間:每個進程都有自己獨立的虛擬空間,而且每個進程只能訪問自己的空間地址,這樣就有效的做到了進程隔離。

分段

基本思路: 把一段與程序所需要的內存空間大小的虛擬空間映射到某個地址空間。虛擬空間的每個字節對應物理空間的每個字節。這個映射過程由軟件來完成。

比如A需要10M,就假設有0x00000000 到0x00A00000大小的虛擬空間,然後從物理內存分配一個相同大小的空間,比如是0x00100000到0x00B00000。操作系統來設置這個映射函數,實際的地址轉換由硬件完成。如果越界,硬件就會判斷這是一個非法訪問,拒絕這個地址請求,並上報操作系統或監控程序。

"

在早期的計算機中,程序是直接運行在物理內存上的,也就是說:程序在運行時訪問的地址就是物理地址。這樣也就是單運行的時候沒有什麼問題!可是,計算機會有多到程序、分時系統和多任務,當我們能夠同時運行多個程序時,CPU的利用率將會比較高。那麼有一個非常嚴重的問題:如何將計算機的有限的物理內存分配給多個程序使用

假設我們計算有128MB內存,程序A需要10MB,程序B需要100MB,程序C需要20MB。如果我們需要同時運行程序A和B,那麼比較直接的做法是將內存的前10MB分配給程序A,10MB~110MB分配給B。

內存不夠這麼辦?從程序的底層來分析

但這樣做,會造成以下問題:

  • 當多個程序需要運行時,必須保證這些程序用到的內存總量要小於計算機實際的物理內存的大小。
  • 進程地址空間不隔離,由於程序是直接訪問物理內存的,所以每一個進程都可以修改其他進程的內存數據,設置修改內核地址空間中的數據,所以有些惡意程序可以隨意修改別的進程,就會造成一些破壞
  • 內存使用效率低 內存空間不足,就需要將其他程序展示拷貝到硬盤當中,然後將新的程序裝入內存。然而由於大量的數據裝入裝出,內存的使用效率會非常低
  • 程序運行的地址不確定;因為內存地址是隨機分配的,所以程序運行的地址也是不正確的

解決這幾個問題的思路就是使用我們非常牛逼的方法:增加中間層 - 即使用一種間接的地址訪問方式。

把程序給出的地址看做是一種虛擬地址,然後通過某種映射,將這個虛擬地址轉化到實際的物理地址。這樣,只需要控制好映射過程,就能保證程序所能訪問的物理內存區域跟別的程序不重疊,達到空間隔離的效果。

隔離

普通的程序它只需要一個簡單的執行環境,一個單一的地址空間,有自己的CPU。

地址空間比較抽象,如果把它想象成一個數組,每一個數組是一字節,數組大小就是地址空間的長度,那麼32位的地址空間大小就是2^32=4294967296字節,即4G,地址空間有效位是0x00000000~0xFFFFFFFF。

地址空間分為兩種:

  • 物理空間:就是物理內存。32位的機器,地址線就有32條,物理空間4G,但如果只裝有512M的內存,那麼實際有效的空間地址就是0x00000000~0x1FFFFFFF,其他部分都是無效的。
  • 虛擬空間:每個進程都有自己獨立的虛擬空間,而且每個進程只能訪問自己的空間地址,這樣就有效的做到了進程隔離。

分段

基本思路: 把一段與程序所需要的內存空間大小的虛擬空間映射到某個地址空間。虛擬空間的每個字節對應物理空間的每個字節。這個映射過程由軟件來完成。

比如A需要10M,就假設有0x00000000 到0x00A00000大小的虛擬空間,然後從物理內存分配一個相同大小的空間,比如是0x00100000到0x00B00000。操作系統來設置這個映射函數,實際的地址轉換由硬件完成。如果越界,硬件就會判斷這是一個非法訪問,拒絕這個地址請求,並上報操作系統或監控程序。

內存不夠這麼辦?從程序的底層來分析

這樣一來利用分段的方式可以解決之前的地址空間不隔離程序運行地址不確定

  • 首先做到了地址隔離,因為A和B被映射到了兩塊不同的物理空間,它們之間沒有任何重疊,如果A訪問虛擬空間的地址超過了0x00A00000這個範圍,硬件就會判斷這是一個非法的訪問,並將這個請求報告給操作系統或者監控程序,由它決定如何處理。
  • 再者,對於每個程序來說,無論它們被分配到地址空間的哪一個區域,對於程序來說都是透明的,它們不需要關心物理地址的變化,它們只要按照從地址0x00000000到0x00A00000來編寫程序、放置變量,所以程序不需要重定位。

第二問題內存使用效率問題依舊沒有解決。

但是分段的方法沒有解決內存使用效率的問題。分段對於內存區域的映射還是按照程序為單位,如果內存不足,被換入換出的磁盤的都是整個程序,這樣勢必會造成大量的磁盤訪問操作,從而嚴重影響速度,這種方法還是顯得粗糙,粒度比較大。事實上根據程序的局部性原理,當一個程序正在運行時,在某個時間段內,它只是頻繁用到了一小部分數據,也就是說,程序的很多數據其實在一個時間段內是不會被用到的。人們很自然地想到了更小粒度的內存分割和映射方法,使得程序的局部性原理得到充分利用,大大提高了內存的使用率。這種方法就是分頁。

分頁

分頁的基本方法是把地址空間人為得等分成固定大小的頁,每一個頁的大小由硬件決定,或硬件支持多種頁的大小,由操作系統選擇決定頁的大小。 目前幾乎所有PC的操作系統都是用4KB大小的頁。我們使用的PC機是32位虛擬地址空間,也就是4GB,按4KB分頁,總共有1048576個頁。

那麼,當我們把進程的虛擬地址空間按頁分割,把常用的數據和代碼裝載到內存中,把不常用的代碼和數據保存在磁盤裡,當需要用到的時候再把它們從磁盤裡取出即可。圖中的線表示映射關係,我們可以看到虛擬空間有些頁被映射到同一個物理頁,這樣就可以實現內存共享。

虛擬頁,物理頁,磁盤頁根據內存空間不一樣而區分

我們可以看到Process 1 的VP2和VP3不在內存中,但是當進程需要用到這兩個頁的時候,硬件就會捕獲到這個消息,就是所謂的頁錯誤(Page Fault),然後操作系統接管進程,負責將VP2和VP3從磁盤讀取出來裝入內存,然都將內存中的這兩個頁和VP2和VP3建立映射關係。以頁為單位存取和交換數據非常方便,硬件本身就支持這種以頁為單位的操作方式。

"

在早期的計算機中,程序是直接運行在物理內存上的,也就是說:程序在運行時訪問的地址就是物理地址。這樣也就是單運行的時候沒有什麼問題!可是,計算機會有多到程序、分時系統和多任務,當我們能夠同時運行多個程序時,CPU的利用率將會比較高。那麼有一個非常嚴重的問題:如何將計算機的有限的物理內存分配給多個程序使用

假設我們計算有128MB內存,程序A需要10MB,程序B需要100MB,程序C需要20MB。如果我們需要同時運行程序A和B,那麼比較直接的做法是將內存的前10MB分配給程序A,10MB~110MB分配給B。

內存不夠這麼辦?從程序的底層來分析

但這樣做,會造成以下問題:

  • 當多個程序需要運行時,必須保證這些程序用到的內存總量要小於計算機實際的物理內存的大小。
  • 進程地址空間不隔離,由於程序是直接訪問物理內存的,所以每一個進程都可以修改其他進程的內存數據,設置修改內核地址空間中的數據,所以有些惡意程序可以隨意修改別的進程,就會造成一些破壞
  • 內存使用效率低 內存空間不足,就需要將其他程序展示拷貝到硬盤當中,然後將新的程序裝入內存。然而由於大量的數據裝入裝出,內存的使用效率會非常低
  • 程序運行的地址不確定;因為內存地址是隨機分配的,所以程序運行的地址也是不正確的

解決這幾個問題的思路就是使用我們非常牛逼的方法:增加中間層 - 即使用一種間接的地址訪問方式。

把程序給出的地址看做是一種虛擬地址,然後通過某種映射,將這個虛擬地址轉化到實際的物理地址。這樣,只需要控制好映射過程,就能保證程序所能訪問的物理內存區域跟別的程序不重疊,達到空間隔離的效果。

隔離

普通的程序它只需要一個簡單的執行環境,一個單一的地址空間,有自己的CPU。

地址空間比較抽象,如果把它想象成一個數組,每一個數組是一字節,數組大小就是地址空間的長度,那麼32位的地址空間大小就是2^32=4294967296字節,即4G,地址空間有效位是0x00000000~0xFFFFFFFF。

地址空間分為兩種:

  • 物理空間:就是物理內存。32位的機器,地址線就有32條,物理空間4G,但如果只裝有512M的內存,那麼實際有效的空間地址就是0x00000000~0x1FFFFFFF,其他部分都是無效的。
  • 虛擬空間:每個進程都有自己獨立的虛擬空間,而且每個進程只能訪問自己的空間地址,這樣就有效的做到了進程隔離。

分段

基本思路: 把一段與程序所需要的內存空間大小的虛擬空間映射到某個地址空間。虛擬空間的每個字節對應物理空間的每個字節。這個映射過程由軟件來完成。

比如A需要10M,就假設有0x00000000 到0x00A00000大小的虛擬空間,然後從物理內存分配一個相同大小的空間,比如是0x00100000到0x00B00000。操作系統來設置這個映射函數,實際的地址轉換由硬件完成。如果越界,硬件就會判斷這是一個非法訪問,拒絕這個地址請求,並上報操作系統或監控程序。

內存不夠這麼辦?從程序的底層來分析

這樣一來利用分段的方式可以解決之前的地址空間不隔離程序運行地址不確定

  • 首先做到了地址隔離,因為A和B被映射到了兩塊不同的物理空間,它們之間沒有任何重疊,如果A訪問虛擬空間的地址超過了0x00A00000這個範圍,硬件就會判斷這是一個非法的訪問,並將這個請求報告給操作系統或者監控程序,由它決定如何處理。
  • 再者,對於每個程序來說,無論它們被分配到地址空間的哪一個區域,對於程序來說都是透明的,它們不需要關心物理地址的變化,它們只要按照從地址0x00000000到0x00A00000來編寫程序、放置變量,所以程序不需要重定位。

第二問題內存使用效率問題依舊沒有解決。

但是分段的方法沒有解決內存使用效率的問題。分段對於內存區域的映射還是按照程序為單位,如果內存不足,被換入換出的磁盤的都是整個程序,這樣勢必會造成大量的磁盤訪問操作,從而嚴重影響速度,這種方法還是顯得粗糙,粒度比較大。事實上根據程序的局部性原理,當一個程序正在運行時,在某個時間段內,它只是頻繁用到了一小部分數據,也就是說,程序的很多數據其實在一個時間段內是不會被用到的。人們很自然地想到了更小粒度的內存分割和映射方法,使得程序的局部性原理得到充分利用,大大提高了內存的使用率。這種方法就是分頁。

分頁

分頁的基本方法是把地址空間人為得等分成固定大小的頁,每一個頁的大小由硬件決定,或硬件支持多種頁的大小,由操作系統選擇決定頁的大小。 目前幾乎所有PC的操作系統都是用4KB大小的頁。我們使用的PC機是32位虛擬地址空間,也就是4GB,按4KB分頁,總共有1048576個頁。

那麼,當我們把進程的虛擬地址空間按頁分割,把常用的數據和代碼裝載到內存中,把不常用的代碼和數據保存在磁盤裡,當需要用到的時候再把它們從磁盤裡取出即可。圖中的線表示映射關係,我們可以看到虛擬空間有些頁被映射到同一個物理頁,這樣就可以實現內存共享。

虛擬頁,物理頁,磁盤頁根據內存空間不一樣而區分

我們可以看到Process 1 的VP2和VP3不在內存中,但是當進程需要用到這兩個頁的時候,硬件就會捕獲到這個消息,就是所謂的頁錯誤(Page Fault),然後操作系統接管進程,負責將VP2和VP3從磁盤讀取出來裝入內存,然都將內存中的這兩個頁和VP2和VP3建立映射關係。以頁為單位存取和交換數據非常方便,硬件本身就支持這種以頁為單位的操作方式。

內存不夠這麼辦?從程序的底層來分析

  • 保護頁也是頁映射的目的之一,簡單地說就是每個頁可以設置權限屬性,誰可以修改,誰可以訪問,而且只有操作系統有權修改這些屬性,那麼操作系統就可以做到保護自己和保護進程。
  • 虛擬存儲的實現需要硬件支持,幾乎所有CPU都採用稱為MMU的部件來進行頁的映射:
"

在早期的計算機中,程序是直接運行在物理內存上的,也就是說:程序在運行時訪問的地址就是物理地址。這樣也就是單運行的時候沒有什麼問題!可是,計算機會有多到程序、分時系統和多任務,當我們能夠同時運行多個程序時,CPU的利用率將會比較高。那麼有一個非常嚴重的問題:如何將計算機的有限的物理內存分配給多個程序使用

假設我們計算有128MB內存,程序A需要10MB,程序B需要100MB,程序C需要20MB。如果我們需要同時運行程序A和B,那麼比較直接的做法是將內存的前10MB分配給程序A,10MB~110MB分配給B。

內存不夠這麼辦?從程序的底層來分析

但這樣做,會造成以下問題:

  • 當多個程序需要運行時,必須保證這些程序用到的內存總量要小於計算機實際的物理內存的大小。
  • 進程地址空間不隔離,由於程序是直接訪問物理內存的,所以每一個進程都可以修改其他進程的內存數據,設置修改內核地址空間中的數據,所以有些惡意程序可以隨意修改別的進程,就會造成一些破壞
  • 內存使用效率低 內存空間不足,就需要將其他程序展示拷貝到硬盤當中,然後將新的程序裝入內存。然而由於大量的數據裝入裝出,內存的使用效率會非常低
  • 程序運行的地址不確定;因為內存地址是隨機分配的,所以程序運行的地址也是不正確的

解決這幾個問題的思路就是使用我們非常牛逼的方法:增加中間層 - 即使用一種間接的地址訪問方式。

把程序給出的地址看做是一種虛擬地址,然後通過某種映射,將這個虛擬地址轉化到實際的物理地址。這樣,只需要控制好映射過程,就能保證程序所能訪問的物理內存區域跟別的程序不重疊,達到空間隔離的效果。

隔離

普通的程序它只需要一個簡單的執行環境,一個單一的地址空間,有自己的CPU。

地址空間比較抽象,如果把它想象成一個數組,每一個數組是一字節,數組大小就是地址空間的長度,那麼32位的地址空間大小就是2^32=4294967296字節,即4G,地址空間有效位是0x00000000~0xFFFFFFFF。

地址空間分為兩種:

  • 物理空間:就是物理內存。32位的機器,地址線就有32條,物理空間4G,但如果只裝有512M的內存,那麼實際有效的空間地址就是0x00000000~0x1FFFFFFF,其他部分都是無效的。
  • 虛擬空間:每個進程都有自己獨立的虛擬空間,而且每個進程只能訪問自己的空間地址,這樣就有效的做到了進程隔離。

分段

基本思路: 把一段與程序所需要的內存空間大小的虛擬空間映射到某個地址空間。虛擬空間的每個字節對應物理空間的每個字節。這個映射過程由軟件來完成。

比如A需要10M,就假設有0x00000000 到0x00A00000大小的虛擬空間,然後從物理內存分配一個相同大小的空間,比如是0x00100000到0x00B00000。操作系統來設置這個映射函數,實際的地址轉換由硬件完成。如果越界,硬件就會判斷這是一個非法訪問,拒絕這個地址請求,並上報操作系統或監控程序。

內存不夠這麼辦?從程序的底層來分析

這樣一來利用分段的方式可以解決之前的地址空間不隔離程序運行地址不確定

  • 首先做到了地址隔離,因為A和B被映射到了兩塊不同的物理空間,它們之間沒有任何重疊,如果A訪問虛擬空間的地址超過了0x00A00000這個範圍,硬件就會判斷這是一個非法的訪問,並將這個請求報告給操作系統或者監控程序,由它決定如何處理。
  • 再者,對於每個程序來說,無論它們被分配到地址空間的哪一個區域,對於程序來說都是透明的,它們不需要關心物理地址的變化,它們只要按照從地址0x00000000到0x00A00000來編寫程序、放置變量,所以程序不需要重定位。

第二問題內存使用效率問題依舊沒有解決。

但是分段的方法沒有解決內存使用效率的問題。分段對於內存區域的映射還是按照程序為單位,如果內存不足,被換入換出的磁盤的都是整個程序,這樣勢必會造成大量的磁盤訪問操作,從而嚴重影響速度,這種方法還是顯得粗糙,粒度比較大。事實上根據程序的局部性原理,當一個程序正在運行時,在某個時間段內,它只是頻繁用到了一小部分數據,也就是說,程序的很多數據其實在一個時間段內是不會被用到的。人們很自然地想到了更小粒度的內存分割和映射方法,使得程序的局部性原理得到充分利用,大大提高了內存的使用率。這種方法就是分頁。

分頁

分頁的基本方法是把地址空間人為得等分成固定大小的頁,每一個頁的大小由硬件決定,或硬件支持多種頁的大小,由操作系統選擇決定頁的大小。 目前幾乎所有PC的操作系統都是用4KB大小的頁。我們使用的PC機是32位虛擬地址空間,也就是4GB,按4KB分頁,總共有1048576個頁。

那麼,當我們把進程的虛擬地址空間按頁分割,把常用的數據和代碼裝載到內存中,把不常用的代碼和數據保存在磁盤裡,當需要用到的時候再把它們從磁盤裡取出即可。圖中的線表示映射關係,我們可以看到虛擬空間有些頁被映射到同一個物理頁,這樣就可以實現內存共享。

虛擬頁,物理頁,磁盤頁根據內存空間不一樣而區分

我們可以看到Process 1 的VP2和VP3不在內存中,但是當進程需要用到這兩個頁的時候,硬件就會捕獲到這個消息,就是所謂的頁錯誤(Page Fault),然後操作系統接管進程,負責將VP2和VP3從磁盤讀取出來裝入內存,然都將內存中的這兩個頁和VP2和VP3建立映射關係。以頁為單位存取和交換數據非常方便,硬件本身就支持這種以頁為單位的操作方式。

內存不夠這麼辦?從程序的底層來分析

  • 保護頁也是頁映射的目的之一,簡單地說就是每個頁可以設置權限屬性,誰可以修改,誰可以訪問,而且只有操作系統有權修改這些屬性,那麼操作系統就可以做到保護自己和保護進程。
  • 虛擬存儲的實現需要硬件支持,幾乎所有CPU都採用稱為MMU的部件來進行頁的映射:
內存不夠這麼辦?從程序的底層來分析

在頁映射模式下,CPU發出的是Virtual Address,即我們程序看到的是虛擬地址。經過MMU轉換以後就變成了Physical Address。一般MMU集成在CPU內部,不會以獨立的部件存在。

"

相關推薦

推薦中...