虛擬記憶體管理【轉】

来源:http://www.cnblogs.com/litaozijin/archive/2017/06/06/6953111.html
-Advertisement-
Play Games

現代操作系統普遍採用虛擬記憶體管理(Virtual Memory Management)機制,這需要處理器中的MMU(Memory Management Unit,記憶體管理單元)提供支持。首先引入 PA 和 VA 兩個概念。 1.PA(Physical Address) 物理地址 如果處理器沒有MMU ...


  現代操作系統普遍採用虛擬記憶體管理(Virtual Memory Management)機制,這需要處理器中的MMU(Memory Management Unit,記憶體管理單元)提供支持。首先引入 PA 和 VA 兩個概念。

1.PA(Physical Address)---物理地址

  如果處理器沒有MMU,或者有MMU但沒有啟用,CPU執行單元發出的記憶體地址將直接傳到晶元引腳上,被記憶體晶元(以下稱為物理記憶體,以便與虛擬記憶體區分)接收,這稱為PA(Physical Address,以下簡稱PA),如下圖所示。

物理地址

物理地址

2.VA(Virtual Address)---虛擬地址

  如果處理器啟用了MMU,CPU執行單元發出的記憶體地址將被MMU截獲,從CPU到MMU的地址稱為虛擬地址(Virtual Address,以下簡稱VA),而MMU將這個地址翻譯成另一個地址發到CPU晶元的外部地址引腳上,也就是將VA映射成PA,如下圖所示。

虛擬地址

虛擬地址

  如果是32位處理器,則內地址匯流排是32位的,與CPU執行單元相連(圖中只是示意性地畫了4條地址線),而經過MMU轉換之後的外地址匯流排則不一定是32位的。也就是說,虛擬地址空間和物理地址空間是獨立的,32位處理器的虛擬地址空間是4GB,而物理地址空間既可以大於也可以小於4GB。

  MMU將VA映射到PA是以頁(Page)為單位的,32位處理器的頁尺寸通常是4KB。例如,MMU可以通過一個映射項將VA的一頁0xb7001000~0xb7001fff映射到PA的一頁0x2000~0x2fff,如果CPU執行單元要訪問虛擬地址0xb7001008,則實際訪問到的物理地址是0x2008。物理記憶體中的頁稱為物理頁面或者頁幀(Page Frame)。虛擬記憶體的哪個頁面映射到物理記憶體的哪個頁幀是通過頁表(Page Table)來描述的,頁表保存在物理記憶體中,MMU會查找頁表來確定一個VA應該映射到什麼PA。

 

3. 進程地址空間

進程地址空間進程地址空間

 

  x86平臺的虛擬地址空間是0x0000 0000~0xffff ffff,大致上前3GB(0x0000 0000~0xbfff ffff)是用戶空間,後1GB(0xc000 0000~0xffff ffff)是內核空間。

   Text Segmest 和 Data Segment

  • Text Segment,包含.text段、.rodata段、.plt段等。是從/bin/bash載入到記憶體的,訪問許可權為r-x。
  • Data Segment,包含.data段、.bss段等。也是從/bin/bash載入到記憶體的,訪問許可權為rw-。

     堆和棧

  • 堆(heap):堆說白了就是電腦記憶體中的剩餘空間,malloc函數動態分配記憶體是在這裡分配的。在動態分配記憶體時堆空間是可以向高地址增長的。堆空間的地址上限稱為Break,堆空間要向高地址增長就要抬高Break,映射新的虛擬記憶體頁面到物理記憶體,這是通過系統調用brk實現的,malloc函數也是調用brk向內核請求分配記憶體的。
  • 棧(stack):棧是一個特定的記憶體區域,其中高地址的部分保存著進程的環境變數和命令行參數,低地址的部分保存函數棧幀,棧空間是向低地址增長的,但顯然沒有堆空間那麼大的可供增長的餘地,因為實際的應用程式動態分配大量記憶體的並不少見,但是有幾十層深的函數調用並且每層調用都有很多局部變數的非常少見。

  如果寫程式的時候沒有註意好記憶體的分配問題,在堆和棧這兩個地方可能產生以下幾種問題:

  1. 記憶體泄露:如果你在一個函數里通過 malloc 在堆里申請了一塊空間,併在棧里聲明一個指針變數保存它,那麼當該函數結束時,該函數的成員變數將會被釋放,包括這個指針變數,那麼這塊空間也就找不回來了,也就無法得到釋放。久而久之,可能造成下麵的記憶體泄露問題
  2. 棧溢出:如果你放太多數據到棧中(例如大型的結構體和數組),那麼就可能會造成“棧溢出”(Stack Overflow)問題,程式也將會終止。為了避免這個問題,在聲明這類變數時應使用 malloc 申請堆的空間。
  3. 野指針 和 段錯誤:如果一個指針所指向的空間已經被釋放,此時再試圖用該指針訪問已經被釋放了的空間將會造成“段錯誤”(Segment Fault)問題。此時指針已經變成野指針,應該及時手動將野指針置空。

4. 虛擬記憶體管理的作用

  1. 虛擬記憶體管理可以控制物理記憶體的訪問許可權。物理記憶體本身是不限制訪問的,任何地址都可以讀寫,而操作系統要求不同的頁面具有不同的訪問許可權,這是利用CPU模式和MMU的記憶體保護機制實現的。
  2. 虛擬記憶體管理最主要的作用是讓每個進程有獨立的地址空間。所謂獨立的地址空間是指,不同進程中的同一個VA被MMU映射到不同的PA,並且在某一個進程中訪問任何地址都不可能訪問到另外一個進程的數據,這樣使得任何一個進程由於執行錯誤指令或惡意代碼導致的非法記憶體訪問都不會意外改寫其它進程的數據,不會影響其它進程的運行,從而保證整個系統的穩定性。另一方面,每個進程都認為自己獨占整個虛擬地址空間,這樣鏈接器和載入器的實現會比較容易,不必考慮各進程的地址範圍是否衝突。
進程地址空間是獨立的

進程地址空間是獨立的

  1. VA到PA的映射會給分配和釋放記憶體帶來方便,物理地址不連續的幾塊記憶體可以映射成虛擬地址連續的一塊記憶體。比如要用malloc分配一塊很大的記憶體空間,雖然有足夠多的空閑物理記憶體,卻沒有足夠大的連續空閑記憶體,這時就可以分配多個不連續的物理頁面而映射到連續的虛擬地址範圍。
不連續的PA可以映射為連續的VA

不連續的PA可以映射為連續的VA

  1. 一個系統如果同時運行著很多進程,為各進程分配的記憶體之和可能會大於實際可用的物理記憶體,虛擬記憶體管理使得這種情況下各進程仍然能夠正常運行。因為各進程分配的只不過是虛擬記憶體的頁面,這些頁面的數據可以映射到物理頁面,也可以臨時保存到磁碟上而不占用物理頁面,在磁碟上臨時保存虛擬記憶體頁面的可能是一個磁碟分區,也可能是一個磁碟文件,稱為交換設備(Swap Device)。當物理記憶體不夠用時,將一些不常用的物理頁面中的數據臨時保存到交換設備,然後這個物理頁面就認為是空閑的了,可以重新分配給進程使用,這個過程稱為換出(Page out)。如果進程要用到被換出的頁面,就從交換設備再載入回物理記憶體,這稱為換入(Page in)。換出和換入操作統稱為換頁(Paging),因此: =+

如下圖所示。第一張圖是換出,將物理頁面中的數據保存到磁碟,並解除地址映射,釋放物理頁面。第二張圖是換入,從空閑的物理頁面中分配一個,將磁碟暫存的頁面載入回記憶體,並建立地址映射。

換頁

換頁

5.malloc 和 free

C標準庫函數malloc可以在堆空間動態分配記憶體,它的底層通過brk系統調用向操作系統申請記憶體。動態分配的記憶體用完之後可以用free釋放,更準確地說是歸還給malloc,這樣下次調用malloc時這塊記憶體可以再次被分配。

1 #include <stdlib.h>
2 void *malloc(size_t size);  //返回值:成功返回所分配記憶體空間的首地址,出錯返回NULL
3 void free(void *ptr);
 

malloc的參數size表示要分配的位元組數,如果分配失敗(可能是由於系統記憶體耗盡)則返回NULL。由於malloc函數不知道用戶拿到這塊記憶體要存放什麼類型的數據,所以返回通用指針void *,用戶程式可以轉換成其它類型的指針再訪問這塊記憶體。malloc函數保證它返回的指針所指向的地址滿足系統的對齊要求,例如在32位平臺上返回的指針一定對齊到4位元組邊界,以保證用戶程式把它轉換成任何類型的指針都能用。

動態分配的記憶體用完之後可以用free釋放掉,傳給free的參數正是先前malloc返回的記憶體塊首地址。

示例

舉例如下:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 typedef struct {
 5     int number;
 6     char *msg;
 7 } unit_t;
 8 int main(void)
 9 {
10     unit_t *p = malloc(sizeof(unit_t));
11     if (p == NULL) {
12         printf("out of memory\n");
13         exit(1);
14     }
15     p->number = 3;
16     p->msg = malloc(20);
17     strcpy(p->msg, "Hello world!");
18     printf("number: %d\nmsg: %s\n", p->number, p->msg);
19     free(p->msg);
20     free(p);
21     p = NULL;
22     return 0;
23 }

 

說明

  • unit_t *p = malloc(sizeof(unit_t));這一句,等號右邊是void *類型,等號左邊是unit_t *類型,編譯器會做隱式類型轉換,我們講過void *類型和任何指針類型之間可以相互隱式轉換。
  • 雖然記憶體耗儘是很不常見的錯誤,但寫程式要規範,malloc之後應該判斷是否成功。以後要學習的大部分系統函數都有成功的返回值和失敗的返回值,每次調用系統函數都應該判斷是否成功。
  • free(p);之後,p所指的記憶體空間是歸還了,但是p的值並沒有變,因為從free的函數介面來看根本就沒法改變p的值,p現在指向的記憶體空間已經不屬於用戶,換句話說,p成了野指針,為避免出現野指針,我們應該在free(p);之後手動置p = NULL;
  • 應該先free(p->msg),再free(p)。如果先free(p),p成了野指針,就不能再通過p->msg訪問記憶體了。

6.記憶體泄漏

  如果一個程式長年累月運行(例如網路伺服器程式),並且在迴圈或遞歸中調用malloc分配記憶體,則必須有free與之配對,分配一次就要釋放一次,否則每次迴圈都分配記憶體,分配完了又不釋放,就會慢慢耗盡系統記憶體,這種錯誤稱為記憶體泄漏(Memory Leak)。另外,malloc返回的指針一定要保存好,只有把它傳給free才能釋放這塊記憶體,如果這個指針丟失了,就沒有辦法free這塊記憶體了,也會造成記憶體泄漏。例如:

1 void foo(void)
2 {
3     char *p = malloc(10);
4     ...
5 }

  foo函數返回時要釋放局部變數p的記憶體空間,它所指向的記憶體地址就丟失了,這10個位元組也就沒法釋放了。記憶體泄漏的Bug很難找到,因為它不會像訪問越界一樣導致程式運行錯誤,少量記憶體泄漏並不影響程式的正確運行,大量的記憶體泄漏會使系統記憶體緊缺,導致頻繁換頁,不僅影響當前進程,而且把整個系統都拖得很慢。

  關於malloc和free還有一些特殊情況。malloc(0)這種調用也是合法的,也會返回一個非NULL的指針,這個指針也可以傳給free釋放,但是不能通過這個指針訪問記憶體。free(NULL)也是合法的,不做任何事情,但是free一個野指針是不合法的,例如先調用malloc返回一個指針p,然後連著調用兩次free(p);,則後一次調用會產生運行時錯誤。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • This is an H1 ============= This is an H2 這是 H1 這是 H2 這是 H6 這是一個標題。 1. 這是第一行列表項。 2. 這是第二行列表項。 給出一些例子代碼: return shell_exec("echo $input | $markdown_scr ...
  • 說明: Zabbix監控服務端、客戶端都已經部署完成,被監控主機已經添加,Zabiix監控運行正常。 實現目的: 在Zabbix服務端設置郵件報警,當被監控主機宕機或者達到觸發器預設值時,會自動發送報警郵件到指定郵箱。 具體操作: 以下操作在Zabbix監控服務端進行 備註:Zabbix監控服務端 ...
  • 記一次windows環境使用linux下使用virt-manager軟體的問題 環境:windows server 2008、ubuntu-server 軟體:putty、virt-manager、xming 首先在windows環境安裝xming軟體,安裝步驟都選擇預設即可,安裝完成後點擊開始運行 ...
  • 假設要連接的FTP信息如下: FTP Server: FtpTest User: tester Password: 123123 打開Windows的開始菜單,執行“運行”命令,在對話框中輸入ftp,按下“確定”按鈕將會切換至DOS視窗,出現命令提示符 ftp 鍵入命令連接FTP伺服器: ftp o ...
  • PowerShell是運行在windows平臺的腳本,而Bash是運行在linux平臺的腳本 現在bash能做的事情,PowerShell也能做,PowerShell的強大之處是它可以管理windows伺服器(特別是域domain),現在的開源PowerShell 也可以管理Linux和Mac(通過 ...
  • 1、ext4文件系統在SSD硬碟是最快的 2、IO的演算法修改成noop,操作系統本身不做處理,讓SSD本身處理 echo noop > /sys/block/sdb/queue/scheduler 3、echo 4096 > read_ahead_kb 表示事先預讀數據的Kb數,預設是128 4、e ...
  • 一、NFS服務簡介 NFS是Network File System的縮寫,即網路文件系統。一種使用於分散式文件系統的協定,由sun公司開發,於1984年向外公佈。功能:通過網路讓不同的機器、不同的操作系統能夠彼此分享個別數據,讓應用程式在客戶端通過網路訪問位於服務端磁碟中的數據,是在類Unix系統間 ...
  • 有些文件不喜歡被修改,可以用chattr命令把他鎖定。如lnmp中現在目錄的文件.user.ini,虛擬主機中不喜歡客戶刪除某個目錄等等。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...