背景 By 魯迅 By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex A53,雙核 3. 使用工具:Source Insight 3.5, Visio 1. 概述 ,連續記憶體分配器,用於分配連續的大塊記憶體。 ,會Reserve一片物理記憶體區域: 1. 設備驅 ...
背景
Read the fucking source code!
--By 魯迅A picture is worth a thousand words.
--By 高爾基
說明:
- Kernel版本:4.14
- ARM64處理器,Contex-A53,雙核
- 使用工具:Source Insight 3.5, Visio
1. 概述
Contiguous Memory Allocator, CMA
,連續記憶體分配器,用於分配連續的大塊記憶體。
CMA分配器
,會Reserve一片物理記憶體區域:
- 設備驅動不用時,記憶體管理系統將該區域用於分配和管理可移動類型頁面;
- 設備驅動使用時,用於連續記憶體分配,此時已經分配的頁面需要進行遷移;
此外,CMA分配器
還可以與DMA子系統
集成在一起,使用DMA的設備驅動程式無需使用單獨的CMA API
。
2. 數據結構
內核定義了struct cma
結構,用於管理一個CMA區域
,此外還定義了全局的cma數組
,如下:
struct cma {
unsigned long base_pfn;
unsigned long count;
unsigned long *bitmap;
unsigned int order_per_bit; /* Order of pages represented by one bit */
struct mutex lock;
#ifdef CONFIG_CMA_DEBUGFS
struct hlist_head mem_head;
spinlock_t mem_head_lock;
#endif
const char *name;
};
extern struct cma cma_areas[MAX_CMA_AREAS];
extern unsigned cma_area_count;
base_pfn
:CMA區域物理地址的起始頁幀號;count
:CMA區域總體的頁數;*bitmap
:點陣圖,用於描述頁的分配情況;order_per_bit
:點陣圖中每個bit
描述的物理頁面的order
值,其中頁面數為2^order
值;
來一張圖就會清晰明瞭:
3. 流程分析
3.1 CMA區域創建
3.1.1 方式一 根據dts來配置
之前的文章也都分析過,物理記憶體的描述放置在dts
中,最終會在系統啟動過程中,對dtb
文件進行解析,從而完成記憶體信息註冊。
CMA
的記憶體在dts
中的描述示例如下圖:
在dtb
解析過程中,會調用到rmem_cma_setup
函數:
RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);
3.1.2 方式二 根據參數或巨集配置
可以通過內核參數或配置巨集,來進行CMA區域的創建,最終會調用到cma_declare_contiguous
函數,如下圖:
3.2 CMA添加到Buddy System
在創建完CMA區域
後,該記憶體區域成了保留區域,如果單純給驅動使用,顯然會造成記憶體的浪費,因此記憶體管理模塊會將CMA區域
添加到Buddy System
中,用於可移動頁面的分配和管理。CMA區域
是通過cma_init_reserved_areas
介面來添加到Buddy System
中的。
core_initcall(cma_init_reserved_areas);
core_initcall
巨集將cma_init_reserved_areas
函數放置到特定的段中,在系統啟動的時候會調用到該函數。
3.3 CMA分配/釋放
- CMA分配,入口函數為
cma_alloc
:
- CMA釋放,入口函數為
cma_release
:
函數比較簡單,直接貼上代碼
/**
* cma_release() - release allocated pages
* @cma: Contiguous memory region for which the allocation is performed.
* @pages: Allocated pages.
* @count: Number of allocated pages.
*
* This function releases memory allocated by alloc_cma().
* It returns false when provided pages do not belong to contiguous area and
* true otherwise.
*/
bool cma_release(struct cma *cma, const struct page *pages, unsigned int count)
{
unsigned long pfn;
if (!cma || !pages)
return false;
pr_debug("%s(page %p)\n", __func__, (void *)pages);
pfn = page_to_pfn(pages);
if (pfn < cma->base_pfn || pfn >= cma->base_pfn + cma->count)
return false;
VM_BUG_ON(pfn + count > cma->base_pfn + cma->count);
free_contig_range(pfn, count);
cma_clear_bitmap(cma, pfn, count);
trace_cma_release(pfn, pages, count);
return true;
}
3.4 DMA使用
代碼參考driver/base/dma-contiguous.c
,主要包括的介面有:
/**
* dma_alloc_from_contiguous() - allocate pages from contiguous area
* @dev: Pointer to device for which the allocation is performed.
* @count: Requested number of pages.
* @align: Requested alignment of pages (in PAGE_SIZE order).
* @gfp_mask: GFP flags to use for this allocation.
*
* This function allocates memory buffer for specified device. It uses
* device specific contiguous memory area if available or the default
* global one. Requires architecture specific dev_get_cma_area() helper
* function.
*/
struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
unsigned int align, gfp_t gfp_mask);
/**
* dma_release_from_contiguous() - release allocated pages
* @dev: Pointer to device for which the pages were allocated.
* @pages: Allocated pages.
* @count: Number of allocated pages.
*
* This function releases memory allocated by dma_alloc_from_contiguous().
* It returns false when provided pages do not belong to contiguous area and
* true otherwise.
*/
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
int count);
在上述的介面中,實際調用的就是cma_alloc/cma_release
介面來實現的。
整體來看,CMA分配器還是比較簡單易懂,也不再深入分析。
4.後記
記憶體管理的分析先告一段落,後續可能還會針對某些模塊進一步的研究與完善。
記憶體管理子系統,極其複雜,盤根錯節,很容易就懵圈了,儘管費了不少心力,也只能說略知皮毛。
學習就像是爬山,面對一座高山,可能會有心理障礙,但是當你跨越之後,再看到同樣高的山,心理上你將不再畏懼。
接下來將研究進程管理子系統
,將任督二脈打通。
未來會持續分析內核中的各類框架,併發機制等,敬請關註,一起探討。