1. 程式人生 > >Linux內存管理基本概念

Linux內存管理基本概念

post address 正常 auto 相互轉換 base 資源 影響 ace

1. 前言

內存(memory)在Linux系統中是一種牽涉面極廣的資源,上至應用程序、下至kernel和driver,無不為之魂牽夢繞。加上它天然的稀缺性,導致內存管理(Memory Management,簡稱MM)是linux kernel中非常重要又非常復雜的一個子系統。

重要性就不多說了,Kernel自有分寸。關於復雜性(鑒於Linux kernel優秀的抽象能力),應該不會被普通人(Linux系統的使用者、應用工程師、驅動工程師、輕量級的內核工程師)感知到才對。事實確實如此,Kernel屏蔽掉了大多數的實現細節,盡量以簡單、易用的方式向其它模塊提供memory服務。

不過呢,這個世界上沒有完美的存在,kernel的內存管理也是如此,由於兩方面的原因:一、眾口難調,內存管理有關的需求實在太復雜了;二、CPU、Device和Memory之間糾結的三角戀(參考下面圖片),導致它也(不得不)提供了很多啰裏啰唆的、不易理解的功能(困擾了很多從入門級到資深級的linux軟件工程師)。

技術分享圖片

圖片1 CPU, Device and Memory

基於上面的原因,本站內存管理子系統發布了很多分析文章,以幫助大家理解內存管理有關的概念。不過到目前為止,還缺少一篇索引類的文章,從整體出發,理解Kernel內存管理所需要面對的軟硬件局面、所要解決的問題,以及各個內存管理子模塊的功能和意義。這就是本文的目的。

2. 內存有關的需求總結

在嵌入式系統中,從需求的角度看內存,是非常簡單的,可以總結為兩大類(參考上面的圖片1):

1)CPU有訪問內存的需求,包括從內存中取指、從內存中取數據、向內存中寫數據。相應的數據流為:

CPU<-------------->MMU(Optional)<----------->Memory

2)其它外部設備有訪問內存的需求,包括從內存“讀取”數據、向內存“寫入”數據。這裏的“讀取”和“寫入”加了引號,是因為在大部分情況下,設備不像CPU那樣有智能,不具備直接訪問內存的能力。總結來說,設備有三種途徑訪問內存:

a)由CPU從中中轉,數據流如下(本質上是CPU訪問內存):
Device<---------------->CPU<--------------------Memory

b)由第三方具有智能的模塊(如DMA控制器)中轉,數據流如下:
Device<----------->DMA Controller<--------->Memory

c)直接訪問內存,數據流如下:
Device<---------->IOMMU(Optional)--------->Memory

那麽Linux kernel的內存管理模塊怎麽理解並滿足上面的需求呢?接下來我們將一一梳理。

3. 軟件(Linux kernel內存管理模塊)的角度看內存

3.1 CPU視角

我們先從CPU的需求說起(以當前具有MMU功能的嵌入式Linux平臺為例),看看會向kernel的內存管理模塊提出哪些需求。

? 看到內存

關於內存以及內存管理,最初始的需求是:Linux kernel的核心代碼(主要包括啟動和內存管理),要能看到物理內存。

在MMU使能之前,該需求很容易滿足。

但MMU使能之後、Kernel的內存管理機制ready之前,Kernel看到的是虛擬地址,此時需要一些簡單且有效的機制,建立虛擬內存到物理內存的映射(可以是部分的映射,但要能夠滿足kenrel此時的需要)。

? 管理內存

看到內存之後,下一步就是將它們管理起來。根據不同的內存形態(在物理地址上是否連續、是否具有NUMA內存、是否具有可拔插的內存、等等),可能有不同的管理模型和管理方法。

? 向內核線程/用戶進程提供服務

將內存管理起來之後,就可以向其它人(kernel的其它模塊、內核線程、用戶空間進程、等等)提供服務了,主要包括:
? 以虛擬地址(VA)的形式,為應用程序提供遠大於物理內存的虛擬地址空間(Virtual Address Space)
? 每個進程都有獨立的虛擬地址空間,不會相互影響,進而可提供非常好的內存保護(memory protection)
? 提供內存映射(Memory Mapping)機制,以便把物理內存、I/O空間、Kernel Image、文件等對象映射到相應進程的地址空間中,方便進程的訪問
? 提供公平、高效的物理內存分配(Physical Memory Allocation)算法
? 提供進程間內存共享的方法(以虛擬內存的形式),也稱作Shared Virtual Memory
? 等等

? 更為高級的內存管理需求

欲望是無止境的,在內存管理模塊提供了基礎的內存服務之後,Linux系統(包括kernel線程和用戶進程)已經可以正常work了,更為高級的需求也產生了,例如:
? 內存的熱拔插(memory hotplug)
? 內存的size超過了虛擬地址可尋址的空間怎麽辦(high memory)
? 超大頁(hugetlbpage)的支持
? 利用磁盤作為交換頁以擴大可用內存(各種swap機制和算法)
? 在NUMA系統中通過移動物理頁面位置的方法提升內存的訪問效率(Page migration)
? 內存泄漏的檢查
? 內存碎片的整理
? 內存不足時的處理(oom kill機制)
? 等等

3.2 Device視角

正常情況下,當軟件活動只需要CPU參與時(例如簡單的數學運算、圖像處理等),上面3.1 所涉及內容已經足夠了,無論是用戶空間程序,還是內核空間程序,都可以歡快的運行了。

不過,當軟件操作一些特殊的、可以以自己的方式訪問memory的硬件設備的時候,麻煩就出現了:軟件通過CPU視角獲得memory,並不能直接被這些硬件設備訪問。於是這些硬件設備就提出了需求:

內存管理模塊需要為這些設備提供一些特殊的獲取內存的接口,這些接口可以按照設備所期望的形式組織內存(因而可以被設備訪問),也可以重新映射成CPU視角的形式,以便CPU可以訪問。

這就是我們在編寫設備驅動的時候會經常遇到的DMA mapping功能,其中DMA是Direct Memory Access的所需,表示(memory)可以被設備直接訪問。

另外,在某些應用場景下,內存數據可能會在多個設備間流動,為了提高效率,不能為每個設備都提供一份拷貝,因此內存管理模塊需要提供設備間內存共享(以及相互轉換)的功能。

4. 軟件結構

基於上面章節的需求,Linux kernel從虛擬內存(VM)、DMA mapping以及DMA buffer sharing三個角度,對內存進行管理,如下圖所示:

技術分享圖片

圖片2 VM、DMA mapping和DMA buffer sharing

其中VM是內存管理的主要模塊,也是我們通常意義上所講的狹義“內存管理”,代碼主要分布在mm/以及arch/xxx/mm/兩個目錄下,其中arch/xxx/mm/*提供平臺相關部分的實現,mm/*提供平臺無關部分的實現。

DMA mapping是內存管理的輔助模塊,註要提供dma_alloc_xxx(申請可供設備直接訪問的內存----dma_addr)和dma_map_xxx(是在CPU視角的虛擬內存和dma_addr之間轉換)兩類接口。該模塊的具體實現依賴於設備訪問內存的方式,代碼主要分別在drivers/base/*(通用實現)以及arch/xxx/mm/(平臺相關的實現)。

最後是DMA buffer sharing的機制,用於在不同設備之間共享內存,一般包括兩種方法:

傳統的、利用CPU虛擬地址中轉的方法,例如scatterlist;

dma buffer sharing framework,位於drivers/dma-buf/dma-buf.c中。

有關這些模塊的具體描述,本文後續的文章將會一一展開(有些已經有了),這裏就先結束吧.

Linux內存管理基本概念