【底層原理】Linux內存管理

2021-12-31 良許Linux

作者:羅道文的私房菜

原文連結:http://luodw.cc/2016/02/17/linux-memory/

今天這篇文章主要是之前看linux內核相關知識和博客Gustavo Duarte中。我(指道文)主要是看了這篇博客,並且結合之前的知識,對內存管理的的理解又上升了一個檔次。所以想通過這篇文章總結下。

我們先來看下linux內存布局,此圖比我之前寫的那篇文章寫的布局更詳細

在linux中,每一個進程都被抽象為task_struct結構體,稱為進程描述符,存儲著進程各方面的信息;例如打開的文件,信號以及內存等等;然後task_struct中的一個屬性mm_struct管理著進程的所有虛擬內存,稱為內存描述符。在mm_struct結構體中,存儲著進程各個內存段的開始以及結尾,如上圖所示;這進程使用的物理內存,即常駐內存RSS頁數,這個內存使用的虛擬地址空間VSZ頁數,還有這個進程虛擬內存區域集合和頁表。

從上面這個圖可以看出,進程是有代碼段Text segment,數據段(已初始化的全局,靜態變量),BSS段(未初始化的全局,靜態變量),堆,內存映射區以及棧;

每一塊虛擬內存區(VMA)都是由一塊連續的虛擬地址組成,這些地址從不覆蓋。一個vm_area_struct實例描述了一塊內存區域,包括這塊內存區域的開始以及結尾地址;flags標誌決定了這塊內存的訪問權限和行為;vm_file決定這塊內存是由哪個文件映射的,如果沒有文件映射,則這塊內存為匿名的(anonymous)。上述圖中提到的每個內存段,都對應於一個vm_area_struct結構。如下圖所示

上圖即為/bin/gonzo進程的內存布局。程序的二進位文件映射到代碼段和數據段,代碼段為只讀只執行,不可更改;全局以及靜態的未初始化的變量映射到BSS段,為匿名映射,堆和棧也是匿名映射,因為沒有相應的文件映射;內存映射區可以映射共享庫,映射文件以及匿名映射,所以這塊內存段可以是文件映射也可以是匿名映射。而且不同的文件,映射到不同的vm_area_struct區。

這些vm_area_struct集合存儲在mm_struct中的一個單向鍊表和紅黑樹中;當輸出/proc/pid/maps文件時,只需要遍歷這個鍊表即可。紅黑樹主要是為了快速定位到某一個內存塊,紅黑樹的根存儲在mm_rb域。

之前介紹過,線性地址需要通過頁表才能轉換為物理地址。每個進程的內存描述符也保存了這個進程頁表指針pgd,每一塊虛擬內存頁都和頁表的某一項對應。

虛擬內存是不存儲任何數據的,它只是將地址空間映射到物理內存。物理內存有內核夥伴系統分配,如果一塊物理內存沒有被映射,就可以被夥伴系統分配給虛擬內存。剛分配的物理內存葉框可能是匿名的,存儲進程數據,也可能是也緩存,存儲文件或塊設備的數據。一塊虛擬內存vm_area_struct塊是由連續的虛擬內存頁組成的,而這些虛擬內存塊映射的物理內存卻不一定連續,如下圖所示:

如上圖所示,有三個頁映射到物理內存,還有兩個頁沒有映射,所以常駐內存RSS為12kb,而虛擬內存大小為20kb。對於有映射到物理內存的三個頁的頁表項PTE的Present標誌設為1,而兩個沒有映射物理內存的虛擬內存頁表項的Present位清除。所以這時訪問那兩塊內存,則會導致異常缺頁。

vma就像應用程式和內核的一個契約。當應用程式申請內存或者文件映射時,內核先響應這個請求,分配或更新虛擬內存;但是這些虛擬內存並沒有映射到真實的物理內存。而是等到內存訪問產生一個內存異常缺頁時才真正映射物理內存。即當訪問沒有映射的虛擬內存時,由於頁表項的Present位沒有被設置,所以此時會產生一個缺頁異常。vma記錄和頁表項兩個在解決內存缺頁,釋放內存以及內存swap out都起著重要的作用。下面圖展示了上述情況:

1、一開始堆中只有8kb的內存,而且都已經映射到物理內存;

2、當調用brk()函數擴展堆時,新的頁是沒有映射到物理內存的,

3、當處理器需要訪問一個地址,而且這個地址在上述剛分配的虛擬內存中,這時產生一個缺頁異常;

4、這時進程向夥伴系統申請一頁的物理內存,映射到那塊虛擬內存上,並添加頁表項,設置Present位.

自此,這個內存管理暫時就說到這。總結下:

1、linux進程的內存布局的每個段都是有一個vm_area_struct,而這個實例是由連續的虛擬內存地址組成;

2、當請求內存時,先是擴展vm_area_struct或者新分配一個vm_area_struct,但是並不映射物理內存,只有等到訪問這塊內存時,產生缺頁異常,內核才分配物理內存。

添加良許個人微信即送3套程式設計師必讀資料

→ 精選技術資料共享

→ 高手如雲交流社群

本公眾號全部博文已整理成一個目錄,請在公眾號裡回復「m」獲取!

推薦閱讀:

手機沒網了,卻還能支付,這是什麼原理?

Sampler:Shell命令執行可視化和告警工具

拆了公司發的中秋禮包,我竟然要被全員批評!

5T技術資源大放送!包括但不限於:C/C++,Linux,Python,Java,PHP,人工智慧,單片機,樹莓派,等等。在公眾號內回復「1024」,即可免費獲取!!

相關焦點

  • 嵌入式Linux內存管理的一些知識點總結
    這個內存管理的知識點還真的需要我們專門的去理解一下,今天大家一起來學習學習嵌入式Linux內存管理的知識。1.不涉及linux內核的彙編知識,僅C語言層面解析1.回答:彙編主要處理的是寄存器地址(包括內容)的計算,進行一部分的地址轉換工作(當然,它是重要的);C語言處理了極大部分的系統內存管理工作。
  • Linux內核中的內存管理
    本文對Linux內存管理使用到的一些數據結構和函數作了簡要描述,而不深入到它們的內部。對這些數據結構和函數有了一個總體上的了解後,再針對各項分別作深入了解的時候,也許會簡單一些。Linux內存訪問限制(僅針對32位系統)默認情況下Linux的內核空間映射到4G虛擬地址的最高1G(即0xC0000000 - 0xFFFFFFF)。
  • 別再說你不懂 Linux 內存管理了
    把虛擬地址轉換成內存的物理地址,這中間涉及利用MMU 內存管理單元(Memory Management Unit ) 對虛擬地址分段和分頁(段頁式)地址轉換,關於分段和分頁的具體流程,這裡不再贅述,可以參考任何一本計算機組成原理教材描述。
  • 10分鐘看懂 Java NIO 底層原理
    此文,從底層入手,給各位小夥伴,起底一下,java的四大io模型。需要面試的,或者沒有弄清楚的小夥伴,徹底的有福了。1.1.Java IO讀寫原理無論是Socket的讀寫還是文件的讀寫,在Java層面的應用開發或者是linux系統底層開發,都屬於輸入input和輸出output的處理,簡稱為IO讀寫。在原理上和處理流程上,都是一致的。區別在於參數的不同。
  • 郭健:Linux內存管理系統參數配置之OOM(內存耗盡)
    按照慣例,最後一章是參考文獻,本文的參考文獻都是來自linux內核的Documentation目錄,該目錄下有大量的文檔可以參考,每一篇都值得細細品味。二、什麼是OOMOOM就是out of memory的縮寫,雖然linux kernel有很多的內存管理技巧(從cache中回收、swap out等)來滿足各種應用空間的vm內存需求,但是,當你的系統配置不合理,讓一匹小馬拉大車的時候,linux kernel會運行非常緩慢並且在某個時間點分配page frame的時候遇到內存耗盡、無法分配的狀況
  • 每個程式設計師都該了解一點 Linux 內存管理知識
    ,創下了今年閱讀量最低記錄(不算圖片),為了挽回顏面,我決定再寫一篇,還是從案例入手,介紹 Linux 內存管理機制和一個內存溢出殺手的存在感。在內存極度緊張的時候, sshd 這種不怎麼佔內存的進程也無法倖免。覆巢之下,焉有完卵。OOM Killer 的全稱叫 Out Of Memory Killer,今天簡單給大家介紹下,它是 linux 內核的內存保護機制。當應用程式大量請求內存導致內存不足的時候,通常會觸發 OOM Killer,OOM Killer 會根據相對簡單粗暴的算法殺掉某個進程以解燃眉之急。
  • 別再說你不懂Linux內存管理了,30 張圖給你安排的明明白白
    把虛擬地址轉換成內存的物理地址,這中間涉及利用MMU 內存管理單元(Memory Management Unit ) 對虛擬地址分段和分頁(段頁式)地址轉換,關於分段和分頁的具體流程,這裡不再贅述,可以參考任何一本計算機組成原理教材描述。
  • Linux 進程、線程、文件描述符的底層原理
    Linux 中的進程其實就是一個數據結構,順帶可以理解文件描述符、重定向、管道命令的底層工作原理,最後我們從作業系統的角度看看為什麼說線程和進程基本沒有區別。在作業系統層面,就是新建了一個進程,這個進程將我們編譯出來的可執行文件讀入內存空間,然後執行,最後退出。你編譯好的那個可執行程序只是一個文件,不是進程,可執行文件必須要載入內存,包裝成一個進程才能真正跑起來。
  • 《Linux鐵三角之內存管理》在線視頻課程
    內存管理是Linux裡面最難最晦澀的部分,但是內存管理不清楚,Linux的很多現象又無法解釋,很多環節都無法理清。
  • 別再說你不懂Linux內存管理了,30 張圖給你安排的明明白白
    把虛擬地址轉換成內存的物理地址,這中間涉及利用MMU 內存管理單元(Memory Management Unit ) 對虛擬地址分段和分頁(段頁式)地址轉換,關於分段和分頁的具體流程,這裡不再贅述,可以參考任何一本計算機組成原理教材描述。
  • 為何Cortex-M處理器運行不了linux
    值得注意的是,Cortex-M下的處理器沒有內存管理單元MMU。二、內存管理單元MMU內存管理單元簡稱MMU,它負責虛擬地址到物理地址的映射,並提供硬體機制的內存訪問權限檢查。在多用戶、多進程的作業系統中,MMU使得各個用戶進程都有獨立的地址空間。
  • 學習嵌入式Linux,做底層還是應用?底層要掌握哪些技能?
    3)做驅動,其實不能稱為「做驅動」,而是可以稱為「做底層系統」,做好了這是通殺各行業。比如一個人工作幾年,做過手機、IPTV、會議電視,但是這些產品對他毫無差別,因為他只做底層。當應用出現問題,解決不了時,他就可以從內核角度給他們出主意,提供工具。做底層的發展方向,應該是技術專家。
  • 10 張圖解再談 Linux 物理內存和虛擬內存
    你更想學時間管理,我不配,抱個西瓜去微博學吧。img言歸正傳,上一篇文章 別再說你不懂Linux內存管理了,10張圖給你安排的明明白白! 分析了 Linux 內存管理機制,如果已經忘了的同學還可以回頭看下,並且也強烈建議先閱讀那一篇再來看這一篇。限於篇幅,上一篇沒有深入學習物理內存管理和虛擬內存分配,今天就來學習一下。
  • 騰訊更新了提供給 Linux 的 DMEMFS 內存管理方式提案
    今年 10 月份,騰訊的工程師曾提出了一個將 DMEMFS 作為 Linux "直接內存文件系統(Direct Memory File-System
  • Linux如何調試內存洩漏
    內存洩漏是指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存
  • 萬字長文,別再說你不懂Linux內存管理了(合輯),30 張圖給你安排的明明白白
    段內核空間用戶空間全圖內存數據結構要讓內核管理系統中的虛擬內存,必然要從中抽象出內存管理數據結構,內存管理操作如「分配、釋放等」都基於這些數據結構操作,這裡列舉兩個管理虛擬內存區域的數據結構按字節為單位虛擬內存,一般用於分配小塊內存,釋放內存對應於 kfree ,可以分配連續的物理內存。函數原型在 <linux/kmalloc.h> 中聲明,一般情況下在驅動程序中都是調用 kmalloc() 來給數據結構分配內存 。還記得前面說的 slab 嗎?
  • Linux下找出吃內存的方法總結
    linux下查詢進程佔用的內存方法總結,假設現在有一個「php-cgi」的進程 ,進程id為「25282」。
  • Linux堆內存管理深入分析(二)
    那它又如何能夠根據用戶的請求分配不同大小的內存呢?要想回答清楚這個問題,需要我們完全理解整個glibc malloc的堆內存管理機制,同時,本文的主要目的之一就是希冀解釋清楚這個概念,鑑於這部分內容較多,我將在後文的第5章加以詳細介紹。NOTE:1. Main thread不含有多個heaps所以也就不含有heap_info結構體。
  • Linux內存初始化(上)
    有了armv8架構訪問內存的理解,我們來看下linux在內存這塊的初始化就更容易理解了。創建啟動頁表:在彙編代碼階段的head.S文件中,負責創建映射關係的函數是create_page_tables。彙編結束後的內存映射關係如下圖所示:
  • linux系統下free命令內存計算邏輯
    本篇文章介紹下linux下free命令展示的內存相關計算邏輯。