1. 程式人生 > >Linux的虛擬儲存及動態記憶體管理及共享記憶體

Linux的虛擬儲存及動態記憶體管理及共享記憶體

實體記憶體與虛擬記憶體

雖然應用程式操作的物件是對映到實體記憶體之上的虛擬記憶體,但是處理器直接操作的卻是實體記憶體。所以當用程式訪問一個虛擬地址時,首先必須將虛擬地址轉化成實體地址,然後處理器才能解析地址訪問請求。地址的轉換工作需要通過查詢頁表才能完成,概括地將,地址轉換需要將虛擬地址分段,使每段虛擬地址都作為一個索引指向頁表,而頁表則指向下一級別的頁表或者指向最終的物理頁面。

    linux中使用三級頁表完成地址轉換。利用多級頁表能夠節約地址轉換需佔用的存放空間。如果利用三級頁錶轉換地址,即使64位機器,佔用的空間也很有限。linux使用的機制:

    頂級頁表示頁全域性目錄(PGD),它包含一個pgd_t型別陣列,多數體系結構中pgd_t型別等同於無符號長整型。PGD中的表項指向二級頁目錄中的表項:PMD

    二級頁表是中間頁目錄(PMD),它是個pmd_t型別資料,其中的表項指向PTE中的表項。

    最後一級的頁表簡稱頁表,其中包含了pte_t型別的頁表項,該頁表項指向物理頁面。多數體系結構中,搜尋頁表的工作由硬體完成。每個程序都有自己的頁表,記憶體描述符的pgd域指向的就是程序的頁全域性目錄。

虛擬儲存器

虛擬儲存器提供了三個重要的能力:

1)將主存看成是一個儲存在磁碟上的地址空間的快取記憶體,在主存中只儲存活動區域,並根據需要在磁碟和主存之間來回傳送資料,通過這種方式,高效的使用的主存。

2)為每個程序提供了一致的地址空間,從而簡化了儲存器管理

3)保護了每個程序的地址空間不被其他程序破壞

Linux作業系統同樣也採用了虛擬儲存技術,對一個技術而言,好像可以訪問整個系統的所有實體記憶體,即使單獨一個程序而言,它擁有的地址空間可以遠遠大於系統實體記憶體。

使用者空間與核心空間

4G的地址空間被人為的分為兩個部分,使用者空間和核心空間,

使用者空間從0-3G,核心空間佔據3G-4G.使用者程序通常情況下只能訪問使用者空間的虛擬地址,不能範圍內和空間的虛擬地址

除非使用者程序執行系統呼叫時,切換進核心態執行,可以訪問到核心空間。

每個程序的使用者空間時完全獨立的,互不相關的。

mm_struct

Linux為每個程序都維持了一個單獨的虛擬地址空間,這個虛擬地址空間例包括我們所熟知的程式碼段,資料段,堆疊等。

核心為系統中的每個程序維護一個單獨的資料結構task_struct,裡面除了之前所學到的,還包括一個mm_struct的指標,指向一個結構體mm_struct

這個結構體描述了虛擬儲存器的當前狀態,該結構體裡有兩個欄位我們是關心的:pgd和mmap

pgd指向第一級頁表的基址,mmap指向一個vm_area_structs的連結串列,其中每個vm_area_structs都描述了當前虛擬地址空間的一個區域。

其中的欄位:

vm_end表示該區域的結束處

vm_start:表示該區域的起始處

vm_next:指向連結串列中的下一個區域結構

缺頁中斷

發生缺頁中斷後,執行了那些操作?

1. 檢查要訪問的虛擬地址是否合法

2.查詢/分配一個物理頁

3.填充物理頁內容

4.重新建立對映關係

通俗地來說,你給出的這個要訪問的地址,作業系統會在頁表中查詢,發現此時你要訪問的頁不在記憶體中,觸發缺頁中斷機制,作業系統會給你分配一個新的頁,如果此時記憶體中的頁已經滿了,作業系統會將記憶體中的一頁換出(置於磁碟),然後將你要訪問的該頁換入記憶體。

動態記憶體分配

動態記憶體分配器維護這一個程序的虛擬儲存器區域,稱為堆,並且維護了一個brk指標指向堆的頂部

分配器將堆視為一組不同大小的塊的集合來維護,每個塊就是一個連續的虛擬儲存器片,要麼是已分配的,要麼是空閒的。

分配器分為兩種:

顯式分配器:像malloc,new這種

隱式分配器:要求分配器檢測一個已分配塊何時不再被程式所使用,那麼就釋放這個塊。隱式分配器也叫做垃圾收集器,自動釋放未使用的已分配的塊的過程叫做垃圾收集,像java這種高階語言就依賴此機制。

記憶體分配的原理

從作業系統角度來看,程序分配記憶體有兩種方式,分別由兩個系統呼叫完成:brk和mmap(不考慮共享記憶體)。

1、brk是將資料段(.data)的最高地址指標_edata往高地址推;

2、mmap是在程序的虛擬地址空間中(堆和棧中間,稱為檔案對映區域的地方)找一塊空閒的虛擬記憶體

     這兩種方式分配的都是虛擬記憶體,沒有分配實體記憶體。在第一次訪問已分配的虛擬地址空間的時候,發生缺頁中斷,作業系統負責分配實體記憶體,然後建立虛擬記憶體和實體記憶體之間的對映關係。

在標準C庫中,提供了malloc/free函式分配釋放記憶體,這兩個函式底層是由brk,mmap,munmap這些系統呼叫實現的。


下面以一個例子來說明記憶體分配的原理:

情況一、malloc小於128k的記憶體,使用brk分配記憶體,將_edata往高地址推(只分配虛擬空間,不對應實體記憶體(因此沒有初始化),第一次讀/寫資料時,引起核心缺頁中斷,核心才分配對應的實體記憶體,然後虛擬地址空間建立對映關係),如下圖:

1、程序啟動的時候,其(虛擬)記憶體空間的初始佈局如圖1所示。

      其中,mmap記憶體對映檔案是在堆和棧的中間(例如libc-2.2.93.so,其它資料檔案等),為了簡單起見,省略了記憶體對映檔案。    _edata指標(glibc裡面定義)指向資料段的最高地址。 

2、程序呼叫A=malloc(30K)以後,記憶體空間如圖2:

      malloc函式會呼叫brk系統呼叫,將_edata指標往高地址推30K,就完成虛擬記憶體分配。

      你可能會問:只要把_edata+30K就完成記憶體分配了?

      事實是這樣的,_edata+30K只是完成虛擬地址的分配,A這塊記憶體現在還是沒有物理頁與之對應的,等到程序第一次讀寫A這塊記憶體的時候,發生缺頁中斷,這個時候,核心才分配A這塊記憶體對應的物理頁。也就是說,如果用malloc分配了A這塊內容,然後從來不訪問它,那麼,A對應的物理頁是不會被分配的。 


3、程序呼叫B=malloc(40K)以後,記憶體空間如圖3。

情況二、malloc大於128k的記憶體,使用mmap分配記憶體,在堆和棧之間找一塊空閒記憶體分配(對應獨立記憶體,而且初始化為0),如下圖:

4、程序呼叫C=malloc(200K)以後,記憶體空間如圖4:

      預設情況下,malloc函式分配記憶體,如果請求記憶體大於128K(可由M_MMAP_THRESHOLD選項調節),那就不是去推_edata指標了,而是利用mmap系統呼叫,從堆和棧的中間分配一塊虛擬記憶體。

      這樣子做主要是因為:

      brk分配的記憶體需要等到高地址記憶體釋放以後才能釋放(例如,在B釋放之前,A是不可能釋放的,這就是記憶體碎片產生的原因,什麼時候緊縮看下面),而mmap分配的記憶體可以單獨釋放。

當然,還有其它的好處,也有壞處,再具體下去,有興趣的同學可以去看glibc裡面malloc的程式碼了。 
5、程序呼叫D=malloc(100K)以後,記憶體空間如圖5;
6、程序呼叫free(C)以後,C對應的虛擬記憶體和實體記憶體一起釋放。

7、程序呼叫free(B)以後,如圖7所示:

        B對應的虛擬記憶體和實體記憶體都沒有釋放,因為只有一個_edata指標,如果往回推,那麼D這塊記憶體怎麼辦呢

當然,B這塊記憶體,是可以重用的,如果這個時候再來一個40K的請求,那麼malloc很可能就把B這塊記憶體返回回去了。 
8、程序呼叫free(D)以後,如圖8所示:

        B和D連線起來,變成一塊140K的空閒記憶體。

9、預設情況下:

       當最高地址空間的空閒記憶體超過128K(可由M_TRIM_THRESHOLD選項調節)時,執行記憶體緊縮操作(trim)。在上一個步驟free的時候,發現最高地址空閒記憶體超過128K,於是記憶體緊縮,變成圖9所示。

動態記憶體管理

提到動態記憶體管理,就很容易想到malloc/new,在此整理。

一.brk與mmap系統呼叫

上文剛剛詳細的講解了,mmap系統呼叫用處非常多,比如一個程序的所有動態庫檔案.so的載入,都需要通過mmap系統呼叫對映指定大小的虛擬地址區間,然後將.so程式碼動態對映到這些區域,以供程序其他部分程式碼訪問;這也正是虛存的好處之一,節省了空間,多份程式碼都需要該庫,只需要建立起對映關係即可。

無論是brk還是mmap返回的都是虛擬地址,在第一次訪問這塊地址的時候,會觸發缺頁異常,然後核心為這塊虛擬地址申請並對映物理頁框,建立頁表對映關係,後續對該區間虛擬地址的訪問,通過頁表獲取實體地址,然後就可以在實體記憶體上讀寫了。

二.malloc/free 庫函式

malloc/free是 libc實現的庫函式,主要實現了一套記憶體管理機制,當其管理的記憶體不夠時,通過brk/mmap等系統呼叫向核心申請程序的虛擬地址區間,如果其維護的記憶體能滿足malloc呼叫,則直接返回,free時會將地址塊返回空閒連結串列。

malloc(size) 的時候,這個函式會多分配一塊空間,用於儲存size變數,free的時候,直接通過指標前移一定大小,就可以獲取malloc時儲存的size變數,從而free只需要一個指標作為引數就可以了calloc 庫函式相當於 malloc + memset(0)

三.new/delete/new[]/delete[]

new/delete 是c++ 內建的運算子,相當於增強版的malloc/free. c++是相容c的,一般來說,同樣功能的庫,c++會在安全性和功能性方面比c庫做更多工作。動態記憶體管理這塊也一樣。

new的實現會呼叫malloc,對於基本型別變數,它只是增加了一個cookie結構, 比如需要new的物件大小是 object_size, 則事實上呼叫 malloc 的引數是 object_size + cookie, 這個cookie 結構存放的資訊包括物件大小,物件前後會包含兩個用於檢測記憶體溢位的變數,所有new申請的cookie塊會連結成雙向連結串列。由於內建了記憶體溢位檢測,所以比malloc更安全。

對於自定義型別,new會先申請上述的大小空間,然後呼叫自定義型別的建構函式,對object所在空間進行構造。c++比c強大的一個方面就是c++編譯器可以自動做構造和析構,new運算子會自動計算需要的空間大小,然後根據型別自己呼叫建構函式,如果存在子型別物件,或者存在繼承的基型別,new都會自動呼叫子型別的建構函式和基型別的建構函式完成構造。同樣,delete 操作符根據cookie的size知道object的大小,如果是自定義型別,會呼叫解構函式對object所在空間進行析構,如果有子型別或繼承,自動呼叫子型別和基型別的解構函式,然後將cookie塊從雙向連結串列摘除,最後呼叫 free_dbg 釋放。

new[] 和delete[]是另外兩個操作符,用於陣列型別的動態記憶體獲取和釋放,實現過程類似new/delete 

共享記憶體

Linux中的兩種共享記憶體。一種是我們的IPC通訊System V版本的共享記憶體,另外的一種就是儲存對映I/O(mmap函式),posix的共享記憶體就是建立在mmap之上的。

mmapI/O的描述符間接說明記憶體對映檔案,另外,mmap另外可以在無親緣的程序之間提供共享記憶體區,這樣,類似的兩個程序之間就是可以進行了通訊。

  Linux提供了記憶體對映函式mmap, 它把檔案內容對映到一段記憶體上(準確說是虛擬記憶體上), 通過對這段記憶體的讀取和修改, 實現對檔案的讀取和修改,mmap()系統呼叫使得程序之間可以通過對映一個普通的檔案實現共享記憶體。普通檔案對映到程序地址空間後,程序可以向訪問記憶體的方式對檔案進行訪問,不需要其他系統呼叫(read,write)去操作。

  記憶體對映,簡而言之就是將使用者空間的一段記憶體區域對映到核心空間,對映成功後,使用者對這段記憶體區域的修改可以直接反映到核心空間,相反,核心空間對這段區域的修改也直接反映使用者空間。那麼對於核心空間<—->使用者空間兩者之間需要大量資料傳輸等操作的話效率是非常高的。

mmap系統呼叫並不完全是為了共享記憶體來設計的,它本身提供了不同於一般對普通檔案的訪問的方式,程序可以像讀寫記憶體一樣對普通檔案進行操作,IPC的共享記憶體是純粹為了共享。

mmap用於共享記憶體的方式

1、我們可以使用普通檔案進行提供記憶體對映,例如,open系統呼叫開啟一個檔案,然後進行mmap操作,得到共享記憶體,這種方式適用於任何程序之間。

2、可以使用特殊檔案進行匿名記憶體對映,這個相對的是具有血緣關係的程序之間,當父程序呼叫mmap,然後進行fork,這樣父程序建立的子程序會繼承父程序匿名對映後的地址空間,這樣,父子程序之間就可以進行通訊了。相當於是mmap的返回地址此時是父子程序同時來維護。

3、另外POSIX版本的共享記憶體底層也是使用了mmap。

與systerm V版本的共享記憶體相比:

1、mmap是在磁碟上建立一個檔案,每個程序地址空間中開闢出一塊空間進行對映。而對於shm而言,shm每個程序最終會對映到同一塊實體記憶體。shm儲存在實體記憶體,這樣讀寫的速度要比磁碟要快,但是儲存量不是特別大。

2、相對於shm來說,mmap更加簡單,呼叫更加方便,所以這也是大家都喜歡用的原因。

3、另外mmap有一個好處是當機器重啟,因為mmap把檔案儲存在磁碟上,這個檔案還儲存了作業系統同步的映像,所以mmap不會丟失,但是shmget就會丟失。

相關推薦

Linux虛擬儲存動態記憶體管理共享記憶體

實體記憶體與虛擬記憶體 雖然應用程式操作的物件是對映到實體記憶體之上的虛擬記憶體,但是處理器直接操作的卻是實體記憶體。所以當用程式訪問一個虛擬地址時,首先必須將虛擬地址轉化成實體地址,然後處理器才能解析地址訪問請求。地址的轉換工作需要通過查詢頁表才能完成,概括地將,地址轉換

一臺linux虛擬機器模擬搭建zookeeper叢集可能遇到的問題解決

一、zookeeper叢集搭建 1:上傳zookeeper-3.4.13.tar.gz到/myfile/tool(自己定義的存放工具的目錄)目錄下: 2:解壓zookeeper,執行如下命令: [[email protected] tool]# tar

Linux中KVM的部署安裝,管理VNC的使用

思路:(在有介面的虛擬機器)kvm的安裝->上傳ISO的系統映象->安裝centos->用VNC遠端登入巢狀虛擬機器 1. KVM的部署安裝 第一步:通過ping命令確定自己主機可以上網 第二步:安裝KVM相關安裝包及其作用 yum -y

JNI記憶體管理優化

JVM記憶體和Native記憶體 上面這張圖大家都應該很熟了,下面只講下和JNI有關的部分 程式計數器 記錄正在執行的虛擬機器位元組碼指令的地址(如果正在執行的是本地方法則為空)。 本地方法棧 本地方法棧與 Java 虛擬機器棧類似,它們之間的區別只不過是本地方法棧為本地方法服務。

JVM記憶體管理JAVA效能調優相關筆記

JVM篇 1.JVM記憶體分配:方法區、Java棧、本地方法棧、堆、程式計數器。方法區:在方法區中,儲存了每個類的資訊(包括類的名稱、方法資訊、欄位資訊)、靜態變數、常量以及編譯器編譯後的程式碼等。Java棧:用來儲存方法中的區域性變數(包括在方法中宣告的非靜態變數以及函式形參)。對於基本資料型別的

Python記憶體管理釋放

python話說會自己管理記憶體,實際上,對於佔用很大記憶體的物件,並不會馬上釋放。舉例,a=range(10000*10000),會發現記憶體飆升一個多G,del a 或者a=[]都不能將記憶體降下來。。 del 可以刪除多個變數,del a,b,c,d辦法:import

[轉] Delphi7 記憶體管理 FastMM 研究

Delphi7 記憶體管理及 FastMM 研究[轉] 作者:劉國輝 一、引言       FastMM 是適用於delphi的第三方記憶體管理器,在國外已經是大名鼎鼎,在國內也有許多人在使用或者希望使用,就連 Borland 也在delphi2007拋棄了自己原有的飽受指責

VMWare linux 虛擬機器三種網路,區別

前言虛擬機器網路模式無論是vmware,virtual box,virtual pc等虛擬機器軟體,一般來說,虛擬機器有三種網路模式:1.橋接2.NAT3.Host-Only初學者看到虛擬機器有三種網路,估計就慌了,筆者也是。哪一種網路是適合自己的虛擬機器呢?橋接   

Linux作業系統基礎操作之磁碟管理連結

df命令 df [-ahikHTm] [檔名或目錄]    檢視當前分割槽容量-a:列出所有的檔案系統,包括特有的/proc等檔案系統-h:以GB、MB、KB等格式顯示-i:使用inode顯示結果-H:以M=1000K取代M=1024K的計算方式-T:連同該分割槽的檔案系統一起列出

RTT之記憶體管理異常中斷

記憶體管理分靜態記憶體管理和動態記憶體管理(根據大小又分2種) 靜態記憶體管理:建立、刪除、初始化、解綁、申請和釋放。初始化記憶體池是屬於靜態記憶體管理,與建立記憶體池不同的是,此處記憶體池物件所使用的記憶體空間是由使用者指定的一個緩衝區空間,使用者把緩衝區的指標傳遞給記憶體池物件控制塊,其餘的初始化工作與

Android之記憶體管理優化

轉自  https://www.kotlintc.com/articles/2311?fr=sidebar 一、Android記憶體基礎 實體記憶體與程序記憶體 實體記憶體即移動裝置上的RAM,當啟動一個Android程式時,會啟動一個Dalvik VM程序,系統會

JVM記憶體管理GC機制

一、概述 Java GC(Garbage Collection,垃圾收集,垃圾回收)機制,是Java與C++/C的主要區別之一,作為Java開發者,一般不需要專門編寫記憶體回收和垃圾清理程式碼,對記憶體洩露和溢位的問題,也不需要像C程式設計師那樣戰戰兢兢。經過這麼長時間的發

Linux中RPM軟件包管理安裝

.rpm packages 分享圖片 ado yum linux中 ddb 數據 ack RPM 結構 查詢RPM軟件包信息 安裝,升級,卸載,RPM軟件包 RPM包管理工具 註:以下測試需要安裝rpm軟件倉庫( yum install rpm -y) 查詢已安裝

Java之美[從菜鳥到高手演變]之JVM記憶體管理垃圾回收

很多Java面試的時候,都會問到有關Java垃圾回收的問題,提到垃圾回收肯定要涉及到JVM記憶體管理機制,Java語言的執行效率一直被C、C++程式設計師所嘲笑,其實,事實就是這樣,Java在執行效率方面確實很低,一方面,Java語言採用面向物件思想,這也決定了其必然是開發效

Linux中如何創建、管理查詢用戶和組

查看 shel 登錄名 add pad a13 ext 圖片 com 一、用戶賬號管理與用戶相關的配置文件有兩個,分別是 /etc/passwd 和 /etc/shadow 。前者用於保存用戶名稱,宿主目錄、登錄shell等基本信息,後者用於保存用戶密碼、賬號有效期等信息。

Android記憶體管理機制和記憶體洩漏分析優化

Android中的記憶體管理機制 分配機制 Android為每個程序分配記憶體的時候,採用了彈性的分配方式,也就是剛開始並不會一下分配很多記憶體給每個程序,而是給每一個程序分配一個“夠用”的量。這個量是根據每一個裝置實際的實體記憶體大小來決定的。隨著應用

Linux中什麼是動態網站環境如何部署

開發十年,就只剩下這套架構體系了! >>>   

linux虛擬機器的安裝和管理

先檢查cpu是否有全虛擬化功能 cat /proc/cpuinfo flags裡顯示cpu的功能 裡面有vmx就是有 1.手動安裝虛擬機器 virt-manager 選擇左上角的電腦 選擇第一個選項,並且命名虛擬機器forward是前進的意思 在瀏覽裡選擇映象的

Java虛擬機器筆記-1(Java技術體系&自動記憶體管理機制&記憶體區域與記憶體溢位&垃圾收集器與記憶體分配策略)

世界上沒有完美的程式,但寫程式是不斷追求完美的過程。 Devices(裝置、裝置)、GlassFish(商業相容應用伺服器) 目錄 1. Java技術體系包括: Java技術體系的4個平臺 虛擬機器分類 HotSpot VM 模組化、混合程式設計 多核並行

Linux虛擬儲存技術

標準Linux使用虛擬儲存器技術,這種技術用於提供比計算機系統中實際使用的實體記憶體大得多的記憶體空間。使用者將感覺到好像程式可以使用非常大的記憶體空間,從而使得程式設計人員在寫程式時不用考慮計算機中的實體記憶體的實際容量。 為了支援虛擬儲存管理器的管理,Lin