" />
0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux內(nèi)核的連續(xù)內(nèi)存分配器(CMA)——避免預(yù)留大塊內(nèi)存

Linux閱碼場(chǎng) ? 來(lái)源:未知 ? 作者:李倩 ? 2018-03-27 11:07 ? 次閱讀

這是我2012年上半年寫(xiě)的文章,現(xiàn)在微信公眾號(hào)再次發(fā)表。

在我們使用ARM嵌入式Linux系統(tǒng)的時(shí)候,一個(gè)頭疼的問(wèn)題是GPU,Camera,HDMI等都需要預(yù)留大量連續(xù)內(nèi)存,這部分內(nèi)存平時(shí)不用, 但是一般的做法又必須先預(yù)留著。目前,Marek Szyprowski和Michal Nazarewicz實(shí)現(xiàn)了一套全新的Contiguous Memory Allocator。通過(guò)這套機(jī)制,我們可以做到不預(yù)留內(nèi)存,這些內(nèi)存平時(shí)是可用的,只有當(dāng)需要的時(shí)候才被分配給Camera,HDMI等設(shè)備。下面分析 它的基本代碼流程。

聲明連續(xù)內(nèi)存

內(nèi)核啟動(dòng)過(guò)程中arch/arm/mm/init.c中的arm_memblock_init()會(huì)調(diào)用dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit));

該函數(shù)位于:drivers/base/dma-contiguous.c

其中的size_bytes定義為:

static const unsigned long size_bytes = CMA_SIZE_MBYTES * SZ_1M; 默認(rèn)情況下,CMA_SIZE_MBYTES會(huì)被定義為16MB,來(lái)源于CONFIG_CMA_SIZE_MBYTES=16->

由此可見(jiàn),連續(xù)內(nèi)存區(qū)域也是在內(nèi)核啟動(dòng)的早期,通過(guò)__memblock_alloc_base()拿到的。

另外:

drivers/base/dma-contiguous.c里面的core_initcall()會(huì)導(dǎo)致cma_init_reserved_areas()被調(diào)用:

cma_create_area()會(huì)調(diào)用cma_activate_area(),cma_activate_area()函數(shù)則會(huì)針對(duì)每個(gè)page調(diào)用:

init_cma_reserved_pageblock(pfn_to_page(base_pfn));

這個(gè)函數(shù)則會(huì)通過(guò)set_pageblock_migratetype(page, MIGRATE_CMA)將頁(yè)設(shè)置為MIGRATE_CMA類型的:

同時(shí)其中調(diào)用的__free_pages(page, pageblock_order);最終會(huì)調(diào)用到__free_one_page(page, zone, order, migratetype);相關(guān)的page會(huì)被加到MIGRATE_CMA的free_list上面去:

list_add(&page->lru, &zone->free_area[order].free_list[migratetype]);

申請(qǐng)連續(xù)內(nèi)存

申請(qǐng)連續(xù)內(nèi)存仍然使用標(biāo)準(zhǔn)的arch/arm/mm/dma-mapping.c中定義的dma_alloc_coherent()和dma_alloc_writecombine(),這二者會(huì)間接調(diào)用drivers/base/dma-contiguous.c中的

->

->

int alloc_contig_range(unsigned long start, unsigned long end,

unsigned migratetype)

需要隔離page,隔離page的作用通過(guò)代碼的注釋可以體現(xiàn):

簡(jiǎn)單地說(shuō),就是把相關(guān)的page標(biāo)記為MIGRATE_ISOLATE,這樣buddy系統(tǒng)就不會(huì)再使用他們。

接下來(lái)調(diào)用__alloc_contig_migrate_range()進(jìn)行頁(yè)面隔離和遷移:

其中的函數(shù)migrate_pages()會(huì)完成頁(yè)面的遷移,遷移過(guò)程中通過(guò)傳入的__alloc_contig_migrate_alloc()申請(qǐng)新的page,并將老的page付給新的page:

其中的unmap_and_move()函數(shù)較為關(guān)鍵,它定義在mm/migrate.c中

通過(guò)unmap_and_move(),老的page就被遷移過(guò)去新的page。

接下來(lái)要回收page,回收page的作用是,不至于因?yàn)槟昧诉B續(xù)的內(nèi)存后,系統(tǒng)變得內(nèi)存饑餓:

->

->

釋放連續(xù)內(nèi)存

內(nèi)存釋放的時(shí)候也比較簡(jiǎn)單,直接就是:

arch/arm/mm/dma-mapping.c:

將page交還給buddy。

內(nèi)核內(nèi)存分配的migratetype

內(nèi)核內(nèi)存分配的時(shí)候,帶的標(biāo)志是GFP_,但是GFP_可以轉(zhuǎn)化為migratetype:

之后申請(qǐng)內(nèi)存的時(shí)候,會(huì)對(duì)比遷移類型匹配的free_list:

另外,筆者也編寫(xiě)了一個(gè)測(cè)試程序,透過(guò)它隨時(shí)測(cè)試CMA的功能:

/*

* kernel module helper for testing CMA

*

* Licensed under GPLv2 or later.

*/

#include

#include

#include

#include

#include

#define CMA_NUM 10

static struct device *cma_dev;

static dma_addr_t dma_phys[CMA_NUM];

static void *dma_virt[CMA_NUM];

/* any read request will free coherent memory, eg.

* cat /dev/cma_test

*/

static ssize_t

cma_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)

{

int i;

for (i = 0; i < CMA_NUM; i++) {??

if (dma_virt[i]) {

dma_free_coherent(cma_dev, (i + 1) * SZ_1M, dma_virt[i], dma_phys[i]);

_dev_info(cma_dev, "free virt: %p phys: %p\n", dma_virt[i], (void *)dma_phys[i]);

dma_virt[i] = NULL;

break;

}

}

return 0;

}

/*

* any write request will alloc coherent memory, eg.

* echo 0 > /dev/cma_test

*/

static ssize_t

cma_test_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)

{

int i;

int ret;

for (i = 0; i < CMA_NUM; i++) {??

if (!dma_virt[i]) {

dma_virt[i] = dma_alloc_coherent(cma_dev, (i + 1) * SZ_1M, &dma_phys[i], GFP_KERNEL);

if (dma_virt[i]) {

void *p;

/* touch every page in the allocated memory */

for (p = dma_virt[i]; p

*(u32 *)p = 0;

_dev_info(cma_dev, "alloc virt: %p phys: %p\n", dma_virt[i], (void *)dma_phys[i]);

} else {

dev_err(cma_dev, "no mem in CMA area\n");

ret = -ENOMEM;

}

break;

}

}

return count;

}

static const struct file_operations cma_test_fops = {

.owner = THIS_MODULE,

.read = cma_test_read,

.write = cma_test_write,

};

static struct miscdevice cma_test_misc = {

.name = "cma_test",

.fops = &cma_test_fops,

};

static int __init cma_test_init(void)

{

int ret = 0;

ret = misc_register(&cma_test_misc);

if (unlikely(ret)) {

pr_err("failed to register cma test misc device!\n");

return ret;

}

cma_dev = cma_test_misc.this_device;

cma_dev->coherent_dma_mask = ~0;

_dev_info(cma_dev, "registered.\n");

return ret;

}

module_init(cma_test_init);

static void __exit cma_test_exit(void)

{

misc_deregister(&cma_test_misc);

}

module_exit(cma_test_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");

MODULE_DESCRIPTION("kernel module to help the test of CMA");

MODULE_ALIAS("CMA test");

申請(qǐng)內(nèi)存:

#echo0>/dev/cma_test

釋放內(nèi)存:

#cat/dev/cma_test

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207905
  • 分配器
    +關(guān)注

    關(guān)注

    0

    文章

    192

    瀏覽量

    25611

原文標(biāo)題:宋寶華:Linux內(nèi)核的連續(xù)內(nèi)存分配器(CMA)——避免預(yù)留大塊內(nèi)存

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux內(nèi)核內(nèi)存規(guī)整總結(jié)

    1.前言 伙伴系統(tǒng)作為內(nèi)核最基礎(chǔ)的物理頁(yè)內(nèi)存分配器,具有高效、實(shí)現(xiàn)邏輯簡(jiǎn)介等優(yōu)點(diǎn),其原理頁(yè)也盡可能降低內(nèi)存外部碎片產(chǎn)生,但依然無(wú)法杜絕碎片問(wèn)題。外部碎片帶來(lái)的最大影響就是
    的頭像 發(fā)表于 11-11 11:17 ?1148次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>內(nèi)存</b>規(guī)整總結(jié)

    Linux內(nèi)核內(nèi)存管理之ZONE內(nèi)存分配器

    內(nèi)核中使用ZONE分配器滿足內(nèi)存分配請(qǐng)求。該分配器必須具有足夠的空閑頁(yè)幀,以便滿足各種內(nèi)存大小請(qǐng)
    的頭像 發(fā)表于 02-21 09:29 ?745次閱讀

    Linux內(nèi)核內(nèi)存管理之內(nèi)核連續(xù)物理內(nèi)存分配

    的主要優(yōu)點(diǎn)是避免了外部碎片,而缺點(diǎn)是需要修改內(nèi)核頁(yè)表。顯然,非連續(xù)內(nèi)存區(qū)域的大小必須是4096的倍數(shù)。Linux使用非
    的頭像 發(fā)表于 02-23 09:44 ?750次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核</b><b class='flag-5'>內(nèi)存</b>管理之<b class='flag-5'>內(nèi)核</b>非<b class='flag-5'>連續(xù)</b>物理<b class='flag-5'>內(nèi)存</b><b class='flag-5'>分配</b>

    Linux內(nèi)存系統(tǒng): Linux 內(nèi)存分配算法

    通用對(duì)象的初始化,從而避免了為同一目而對(duì)一個(gè)對(duì)象重復(fù)進(jìn)行初始化 8、slab 高速緩存1) 普通高速緩存· slab 分配器所提供的小塊連續(xù)內(nèi)存
    發(fā)表于 08-24 07:44

    如何去制作一個(gè)高效的內(nèi)存分配器

    高效內(nèi)存分配機(jī)制是什么意思?如何去制作一個(gè)高效的內(nèi)存分配器呢?
    發(fā)表于 01-20 06:57

    內(nèi)存之旅——如何提升CMA利用率?

    內(nèi)存區(qū)域稱為 CMA 區(qū)域;把 CMA 的上層使用者稱為 CMA 業(yè)務(wù)。)一、為什么需要CMA?CMA
    發(fā)表于 03-22 16:26

    為什么需要CMA?CMA具體是如何工作的

    CMA?CMA 全稱是 Contiguous Memory Allocator(連續(xù)內(nèi)存分配器)。顧名思義它是一種
    發(fā)表于 03-23 11:22

    linux內(nèi)存管理中的SLAB分配器詳解

    管理區(qū)頁(yè)框分配器,這里我們簡(jiǎn)稱為頁(yè)框分配器,在頁(yè)框分配器中主要是管理物理內(nèi)存,將物理內(nèi)存的頁(yè)框分配
    發(fā)表于 05-17 15:01 ?2093次閱讀
    <b class='flag-5'>linux</b><b class='flag-5'>內(nèi)存</b>管理中的SLAB<b class='flag-5'>分配器</b>詳解

    深入剖析SLUB分配器和SLAB分配器的區(qū)別

    首先為什么要說(shuō)slub分配器,內(nèi)核里小內(nèi)存分配一共有三種,SLAB/SLUB/SLOB,slub分配器是slab
    發(fā)表于 05-17 16:05 ?1019次閱讀
    深入剖析SLUB<b class='flag-5'>分配器</b>和SLAB<b class='flag-5'>分配器</b>的區(qū)別

    Linux內(nèi)核深度解析》之內(nèi)存地址空間

    內(nèi)核空間提供了把頁(yè)劃分成小內(nèi)存分配的塊分配器,提供分配內(nèi)存的接口 kmalloc()和釋放
    的頭像 發(fā)表于 07-15 14:22 ?2166次閱讀

    bootmem分配器使用的數(shù)據(jù)結(jié)構(gòu)

    內(nèi)核初始化的過(guò)程中需要分配內(nèi)存,內(nèi)核提供了臨時(shí)的引導(dǎo)內(nèi)存分配器,在頁(yè)
    的頭像 發(fā)表于 07-22 11:18 ?1346次閱讀

    Linux之引導(dǎo)內(nèi)存分配器

    早期使用的引導(dǎo)內(nèi)存分配器是 bootmem,目前正在使用 memblock 取代 bootmem。如果開(kāi)啟配置宏 CONFIG_NO_BOOTMEM,memblock 就會(huì)取代 bootmem。為了保證兼容性,bootmem 和 memblock 提供了相同的接口。
    的頭像 發(fā)表于 07-22 11:17 ?1363次閱讀

    Linux內(nèi)核之伙伴分配器

    內(nèi)核初始化完畢后,使用頁(yè)分配器管理物理頁(yè),當(dāng)前使用的頁(yè)分配器是伙伴分配器,伙伴分配器的特點(diǎn)是算法簡(jiǎn)單且效率高。
    的頭像 發(fā)表于 07-25 14:06 ?1606次閱讀

    Linux內(nèi)核之塊分配器

    為了解決小塊內(nèi)存分配問(wèn)題,Linux 內(nèi)核提供了塊分配器,最早實(shí)現(xiàn)的塊分配器是SLAB
    的頭像 發(fā)表于 07-27 09:35 ?1478次閱讀

    Linux內(nèi)核引導(dǎo)內(nèi)存分配器的原理

    Linux內(nèi)核引導(dǎo)內(nèi)存分配器使用的是伙伴系統(tǒng)算法。這種算法是一種用于動(dòng)態(tài)內(nèi)存分配的高效算法,它將
    發(fā)表于 04-03 14:52 ?343次閱讀