这是我2012年上半年写的文章,现在微信公众号再次发表。
在我们使用arm等嵌入式linux系统的时候,一个头疼的问题是gpu,camera,hdmi等都需要预留大量连续内存,这部分内存平时不用, 但是一般的做法又必须先预留着。目前,marek szyprowski和michal nazarewicz实现了一套全新的contiguous memory allocator。通过这套机制,我们可以做到不预留内存,这些内存平时是可用的,只有当需要的时候才被分配给camera,hdmi等设备。下面分析 它的基本代码流程。
声明连续内存
内核启动过程中arch/arm/mm/init.c中的arm_memblock_init()会调用dma_contiguous_reserve(min(arm_dma_limit, arm_lowmem_limit));
该函数位于:drivers/base/dma-contiguous.c
其中的size_bytes定义为:
static const unsigned long size_bytes = cma_size_mbytes * sz_1m; 默认情况下,cma_size_mbytes会被定义为16mb,来源于config_cma_size_mbytes=16->
由此可见,连续内存区域也是在内核启动的早期,通过__memblock_alloc_base()拿到的。
另外:
drivers/base/dma-contiguous.c里面的core_initcall()会导致cma_init_reserved_areas()被调用:
cma_create_area()会调用cma_activate_area(),cma_activate_area()函数则会针对每个page调用:
init_cma_reserved_pageblock(pfn_to_page(base_pfn));
这个函数则会通过set_pageblock_migratetype(page, migrate_cma)将页设置为migrate_cma类型的:
同时其中调用的__free_pages(page, pageblock_order);最终会调用到__free_one_page(page, zone, order, migratetype);相关的page会被加到migrate_cma的free_list上面去:
list_add(&page->lru, &zone->free_area[order].free_list[migratetype]);
申请连续内存
申请连续内存仍然使用标准的arch/arm/mm/dma-mapping.c中定义的dma_alloc_coherent()和dma_alloc_writecombine(),这二者会间接调用drivers/base/dma-contiguous.c中的
->
->
int alloc_contig_range(unsigned long start, unsigned long end,
unsigned migratetype)
需要隔离page,隔离page的作用通过代码的注释可以体现:
简单地说,就是把相关的page标记为migrate_isolate,这样buddy系统就不会再使用他们。
接下来调用__alloc_contig_migrate_range()进行页面隔离和迁移:
其中的函数migrate_pages()会完成页面的迁移,迁移过程中通过传入的__alloc_contig_migrate_alloc()申请新的page,并将老的page付给新的page:
其中的unmap_and_move()函数较为关键,它定义在mm/migrate.c中
通过unmap_and_move(),老的page就被迁移过去新的page。
接下来要回收page,回收page的作用是,不至于因为拿了连续的内存后,系统变得内存饥饿:
->
->
释放连续内存
内存释放的时候也比较简单,直接就是:
arch/arm/mm/dma-mapping.c:
将page交还给buddy。
内核内存分配的migratetype
内核内存分配的时候,带的标志是gfp_,但是gfp_可以转化为migratetype:
之后申请内存的时候,会对比迁移类型匹配的free_list:
另外,笔者也编写了一个测试程序,透过它随时测试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 /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 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 );
module_description(kernel module to help the test of cma);
module_alias(cma test);
申请内存:
#echo0>/dev/cma_test
释放内存:
#cat/dev/cma_test
双输出SLIC电源共享反馈
三星Galaxy Note 10 Lite即将上市,已获得GCF认证
5G全球第一的韩国也有很多争议
让机器人自适应未来环境变化
饭店掀起智能化风潮 其智能化成为在市场上的胜出关键
Linux内核的连续内存分配器(CMA)——避免预留大块内存
最全变频器控制端子接线方法和技巧
连接键盘和鼠标
三星S8edge概念图: 双曲面+高屏占比
五分钟体验本田FUNTEC安全技术
西北地区第一块太阳能光伏玻璃在彩虹顺利下线
磷酸铁锂电池新“归宿” 通信市场规模高达8000亿元
汽车雷达传感器和拥挤的无线电频谱:城市电子战场
7种云存储和文件共享软件分享
烙铁烫焊锡零件的操作方法及注意事项
智能台灯打造护眼新未来,颜值高性能好
诺基亚Android One新机曝光:后置双摄+背部指纹识别
BOE京东方携创维推全球首款主动式玻璃基Mini LED电视
物联网最新技术可穿戴设备可以在任何环境包括外太空使用了
水质传感器市场分析