【原創】(十六)Linux內存管理之CMA


背景

  • Read the fucking source code! --By 魯迅
  • A picture is worth a thousand words. --By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

Contiguous Memory Allocator, CMA,連續內存分配器,用於分配連續的大塊內存。
CMA分配器,會Reserve一片物理內存區域:

  1. 設備驅動不用時,內存管理系統將該區域用於分配和管理可移動類型頁面;
  2. 設備驅動使用時,用於連續內存分配,此時已經分配的頁面需要進行遷移;

此外,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.后記

內存管理的分析先告一段落,后續可能還會針對某些模塊進一步的研究與完善。
內存管理子系統,極其復雜,盤根錯節,很容易就懵圈了,盡管費了不少心力,也只能說略知皮毛。
學習就像是爬山,面對一座高山,可能會有心理障礙,但是當你跨越之后,再看到同樣高的山,心理上你將不再畏懼。

接下來將研究進程管理子系統,將任督二脈打通。

未來會持續分析內核中的各類框架,並發機制等,敬請關注,一起探討。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM