32查运行内存的map文件_linux内存管理
概念先行
先理解內存管理中的幾個概念:內存,主存,緩存,外存,虛擬內存,物理內存,虛擬地址,物理地址
外存:
計算機的外部存儲,比如硬盤(機械硬盤、固態硬盤、混合硬盤),usb
內存:
用于計算機運行過程中的臨時存儲,斷電清除(這也是我們計算機休眠能保留進程狀態,而斷電關機不行的原因)。內存現在特指動態隨機存儲器(DRAM),就是我們電腦的內存條,也就是我們說的物理內存,等價于主存。也就是內存~物理內存~主存~內存條
緩存:
在內存管理子系統中指的是靜態隨機存儲器(SRAM),比如cpu與主存之間的多級緩存(L1, L2 和 L3 高速緩存),當然計算機廣義上的緩存遠不止這些
虛擬內存:
抽象概念,虛擬內存的引入旨在為每個進程提供統一線性一致的地址空間,真正的地址則有MMU(內存管理單元)控制映射至對應的物理內存空間,而虛擬地址,物理地址則代表對應空間的偏移地址
需要注意的是,對于某一特定指令架構的計算機(比如32位),虛擬內存能夠映射的地址,一部分映射至物理內存,當物理內存已滿時,則進行頁面置換映射至對應的磁盤空間。也就是虛擬內存地址包括(物理內存與部分磁盤空間)的地址映射
為什么呢?比如32位系統,內存條只有256M,此時32位系統最多只能用4G內存(虛擬內存):32位系統,即內存地址長度為32,最多映射2^32=2^2*2^30=4G個字節。遠遠大于內存條,則在進程運行過程中,當運行數據超過內存限度,部分數據自動“溢出”,這時系統會將磁盤上的部分空間模擬成虛擬內存,并將原來映射到物理內存中的暫時不運行的程序或不使用的數據置換到磁盤中,騰出更多物理內存空間并重新映射至虛擬內存中被調用
了解了概念,簡單看下內存管理子系統虛擬地址(virtual adress)與物理地址(physical address)的關系
進程訪問某個虛擬地址(虛擬頁號+偏移量),cpu通過內存管理單元(MMU)找到對應到物理內存中的物理地址并返回,過程如下:
- 查詢快表緩存(TLB)找到是否有緩存的頁幀(物理頁號),有則直接返回緩存的內容 
- 否則在頁表(分頁管理)中,找到虛擬地址的虛擬頁號對應的物理頁號的起始地址,返回物理地址(物理地址=物理起始地址+偏移量) 
用戶空間與內核空間
為了避免用戶進程直接操作內核,保證內核安全,操作系統將虛擬內存劃分為兩部分,一部分是內核空間(Kernel-space),一部分是用戶空間(User-space)。在 Linux 系統中,內核模塊運行在內核空間,對應的進程處于內核態;而用戶程序運行在用戶空間,對應的進程處于用戶態。
用戶空間的進程無法直接訪問內核函數,比如由調度系統切換至內核態,才能調用內核函數。這也就是為啥我們說調用glibc庫的fread函數的時候需要會發生上下文切換,并且發生兩次拷貝(磁盤 -> 內核緩沖區(read buffer) -> 用戶緩沖區)的原因
虛擬內存分為用戶空間與內核空間,通常按3:1分配,比如32位系統的能表示的最大虛擬地址為4G,將最高的 1G 的字節(從虛擬地址 0xC0000000 到 0xFFFFFFFF)供內核進程使用,稱為內核空間;而較低的 3G 的字節(從虛擬地址 0x00000000 到 0xBFFFFFFF),供各個用戶進程使用,稱為用戶空間。下圖是一個進程的用戶空間和內核空間的內存布局:
內核空間
內核空間總是駐留在內存中,它是為操作系統的內核保留的。應用程序是不允許直接在該區域進行讀寫或直接調用內核代碼定義的函數的。上圖左側區域為內核進程對應的虛擬內存,按訪問權限可以分為進程私有和進程共享兩塊區域。
- 進程私有的虛擬內存:每個進程都有單獨的內核棧、頁表、task 結構以及 mem_map 結構等。 
- 進程共享的虛擬內存:屬于所有進程共享的內存區域,包括物理存儲器、內核數據和內核代碼區域。 
用戶空間
每個用戶進程都有一個單獨的用戶空間,用戶空間包括以下幾個內存區域:
- 運行時棧:由編譯器自動釋放。存放函數的參數值,局部變量和方法返回值等。每當一個函數被調用時,該函數的返回類型和一些調用的信息被存儲到棧頂,調用結束后調用信息會被彈出彈出并釋放掉內存。棧區是從高地址位向低地址位增長的,是一塊連續的內在區域,最大容量是由系統預先定義好的,申請的棧空間超過這個界限時會提示溢出,比如遞歸申請會出現“棧溢出”現象 
- 內存映射區域:例如將動態庫,共享內存等虛擬空間的內存映射到物理空間的內存,一般是 shm,mmap 函數所分配的虛擬內存空間。 
- 運行時堆:用于存放進程運行中被動態分配的內存段,位于 BSS 和棧中間的地址位。由開發人員申請分配(malloc)和釋放(free)。堆是從低地址位向高地址位增長,采用鏈式存儲結構。頻繁地 malloc/free 造成內存空間的不連續,產生大量碎片。當申請堆空間時,庫函數按照一定的算法搜索可用的足夠大的空間。因此堆的效率比棧要低的多。 
- 未初始化的數據段:存放未初始化的全局變量,BSS 的數據在程序開始執行之前被初始化為 0 或 NULL。 
- 已初始化的數據段:存放已初始化的全局變量,包括靜態全局變量、靜態局部變量以及常量。 
- 代碼段:存放 CPU 可以執行的機器指令,該部分內存只能讀不能寫。通常代碼區是共享的,即其它執行程序可調用它。假如機器中有數個進程運行相同的一個程序,那么它們就可以使用同一個代碼段。 
當我們寫完代碼,編譯,鏈接并且生成可執行文件后,得到的這個東西就是一系列二進制代碼的集合,我們管這東西叫做程序,存儲在磁盤上。只有當我們執行這個文件后,程序才會被操作系統讀入內存運行,比如
[mqq@9-37-26-84 ~]$ size /usr/local/bin/curltext data bss dec hex filename155348 1348 272 156968 26528 /usr/local/bin/curl上面命令輸出的地址就是各個分段的虛擬地址,當進程執行一個程序時,需要先從先內存中讀取該進程的指令,然后執行,獲取指令時用到的就是虛擬地址。這個虛擬地址是程序鏈接時確定的(內核加載并初始化進程時會調整動態庫的地址范圍)。為了獲取到實際的數據,CPU 需要將虛擬地址轉換成物理地址,CPU 轉換地址時需要用到進程的頁表(Page Table),而頁表(Page Table)里面的數據由操作系統維護。
內存管理機制
分段管理(Segmentation)
分段機制下的虛擬地址由兩部分組成,段選擇子和段內偏移量。
- 段選擇子:保存在段寄存器里面。段選擇子里面最重要的是段號,用作段表的索引。段表里面保存的是這個段的基地址、段的界限和特權等級等控制位。 
- 段內偏移量:位于 0 和段界限之間,如果段內偏移量是合法的,則段基地址+段內偏移量=物理內存地址。 
程序若干個邏輯分段(棧,堆,數據等)會映射到對應的物理內存分段,如果要訪問(棧)段 3 中偏移量 500 的虛擬地址,我們可以計算出物理地址為,段 3 在物理內存的基地址 7000 + 偏移量 500 = 7500。
分段的內存碎片問題(內存碎片的產生可自行google)比較嚴重,所以引入了分頁管理(更小顆粒度)
分頁管理
分頁是把整個虛擬和物理內存空間切成一段段固定尺寸的頁大小。在 Linux 下,1頁=4KB。
在分頁機制下的虛擬地址有兩部分組成,分別是頁號和頁內偏移。
- 頁號:作為頁表的索引,映射虛擬頁號到物理頁號在物理內存的基地址 
- 頁內偏移量:上面的基地址+頁內偏移=物理內存地址 
下面舉個例子,虛擬內存中的頁通過頁表映射為了物理內存中的頁,如下圖:
這里講一下MMU中具體的頁表映射邏輯
虛擬頁面中每一項包含多個控制位
VALID 位表示是否緩存
SUP 位表示進程是否必須運行在超級用也就是內核模式下才能訪問該頁
WRITE 位控制頁面的寫訪問
EXRC 位控制頁面的執行
在任意時刻,虛擬頁面分為三個不同的狀態:
未分配的,VM 系統還未分配(或者創建)的頁,未分配的頁沒有任何數據和它們關聯,因此不占用任何內存空間。
緩存的,當前已緩存在物理內存中的已分配頁。
未緩存的,未緩存在物理內存中的已分配頁。
思考個問題,如果進程訪問某個虛擬頁面,但沒有對應的物理內存映射,會發生什么?
會觸發頁中斷,就是我們說的Page Fault
上圖所示,訪問不到虛擬頁的內存的時候
(2)檢測Valid=0,發生缺頁中斷,然后交給缺頁中斷處理器處理邏輯
(3)缺頁中斷處理器,從物理內存中選擇某一合適的頁置換(swap)到磁盤,然后從磁盤中創建新的虛擬頁面,更新虛擬頁與物理頁的映射,然后返回用戶進程。當異常處理程序返回時,它會重啟執行導致缺頁的指令,該指令會將導致缺頁的虛擬地址重新發送到MMU。此時該虛擬頁號已經在主存(物理內存)中了,那么就是頁命中了。
簡單分頁會有空間的問題,因為每個進程都維護自己的頁表,空間浪費,所以引入了多級頁表機制,從而大大減少頁表空間
回到剛才的內存碎片的問題,內存碎片通常分為內部碎片和外部碎片:
- 內部碎片是由于采用固定大小的內存分區,當一個進程不能完全使用分給它的固定內存區域時就產生了內部碎片,通常內部碎片難以完全避免。 
- 外部碎片是由于某些未分配的連續內存區域太小,以至于不能滿足任意進程的內存分配請求,從而不能被進程利用的內存區域。 
一般我們解決的是外部碎片問題,而現在操作系統普遍采用的段頁式內存分配方式,就是將進程的內存區域分為不同的段,然后將每一段由多個固定大小的頁組成。通過頁表機制,使段內的頁可以不必連續處于同一內存區域,從而減少了外部碎片,然而同一頁內仍然可能存在少量的內部碎片,只是一頁的內存空間本就較小,從而使可能存在的內部碎片也較少。
段頁式內存管理
段頁式內存管理機制下,虛擬地址由段號、段內頁號和頁內位移三部分組成。
段頁式地址變換中要得到物理地址須經過三次內存訪問:
- 第一次訪問段表,得到頁表起始地址; 
- 第二次訪問頁表,得到物理頁號; 
- 第三次將物理頁號與頁內位移組合,得到物理地址。 
總的來說,避免外碎片的方法有兩種:
- 利用分頁單元把一組非連續的空閑頁框映射到連續的線性地址 
- 開發一種適當的技術來記錄現存的空閑的連續頁框塊的情況,以盡量避免為滿足對小塊的請求而分割大的空閑快 
第一種方案的意思是,我們使用地址轉換技術,把非連續的物理地址轉換成連續的線性地址。就是上面提到的段頁式內存分配
第二種方案的意思是,開發一種特有的分配技術來記錄下來空閑內存的情況,從而解決內存碎片問題。linux系統下采取了伙伴系統(buddy)和slab以及相關的分配算法,回收機制提高了內存的利用率(具體感興趣的可以自行google)。
整體內存管理架構
總結:
內存分為虛擬內存和物理內存,虛擬內存分成用戶空間和內核空間(3:1),內存管理有分段,分頁,段頁式機制,地址轉換由MMU負責(TLB+頁表)映射,涉及知識點有頁面置換,缺頁錯誤,內存碎片等
要對內存管理子系統有深入的了解,還需要了解虛擬內存管理子系統中的各種機制與算法
buddy (伙伴系統(buddy system):以頁為單位管理和分配內存)
slab(基于伙伴系統分配的大內存進一步細分成小內存分配)
zone(內核地址映射邏輯)
kswapd(頁面換入換出,頁緩存回收,zone掃描)
bdflush(延遲dirty buffers刷盤機制)
但是本篇文章旨在為了對內存管理有個整體的了解,更深入的會放到后面的文章中
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的32查运行内存的map文件_linux内存管理的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 韩国电池正极材料 2022 年出口超过
- 下一篇: 银行卡编码规则及检验算法详解
