2023-06-11  阅读(55)
原文作者:奇小葩 原文地址:https://blog.csdn.net/u012489236/category_9614673.html

在第linux内存管理笔记(十一)—CMA中,我们学习了操作系统预留的大量的连续内存,我们对其引入的原因和使用方法进行了分析,其主要是用于内核中分配连续的大块内存。

  • 当设备驱动不适用时,内存管理系统将该区域用于分配和管理可移动类型页面
  • 当驱动程序使用时,用于连续内存分配,此时已经分配的页面进行迁移

这样既可以在内核中分配连续的大内存,同时又不浪费内存空间,因为当内存不足的时候,可以通过内存迁移实现内存重新分配的方式。通过之前的分析,当启动的时候通过arm_memblock_init->dma_contiguous_reserve->dma_contiguous_reserve_area->dma_contiguous_early_fixup,将CMA的起始地址和空间长度存储在dma_mmu_remap

    void __init dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
    {
    	dma_mmu_remap[dma_mmu_remap_num].base = base;
    	dma_mmu_remap[dma_mmu_remap_num].size = size;
    	dma_mmu_remap_num++;
    }

在paging_init做完低端内存映射后,就调用到dma_contiguous_remap进行相关设置,看看这个函数做了些什么呢?

    void __init dma_contiguous_remap(void)
    {
    	int i;
    	for (i = 0; i < dma_mmu_remap_num; i++) {
    		phys_addr_t start = dma_mmu_remap[i].base;                                   -------------(1)
    		phys_addr_t end = start + dma_mmu_remap[i].size;
    		struct map_desc map;
    		unsigned long addr;
            
    		if (end > arm_lowmem_limit)
    			end = arm_lowmem_limit;
    		if (start >= end)
    			continue;
    
    		map.pfn = __phys_to_pfn(start);                                             -------------(2)
    		map.virtual = __phys_to_virt(start);
    		map.length = end - start;
    		map.type = MT_MEMORY_DMA_READY;
    
    		for (addr = __phys_to_virt(start); addr < __phys_to_virt(end);              -------------(3)
    		     addr += PMD_SIZE)
    			pmd_clear(pmd_off_k(addr));
    
    		flush_tlb_kernel_range(__phys_to_virt(start),                               -------------(4)
    				       __phys_to_virt(end));
    
    		iotable_init(&map, 1);                                                      -------------(5)
    	}
    }
  • 1.这个区域是之前通过dma_contiguous_reserve来设置的保留区域,其start = 0x8c00 0000,length = 0x1400 0000,这个空间大小与dts中配置的"shared-dma-pool"大小一致。
  • 2.配置该区间的Map_desc,为后边的映射create_mapping做准备
  • 3.清0x8c00 0000 - 0xA000 0000空间的pmd表
  • 4.刷新该的区域对应的tlb
  • 5.iotable_init函数来建立页映射关系,主要是CAM的映射

iotable_init函数就是最终实现建立虚拟地址映射的函数,其处理流程如下

    void __init iotable_init(struct map_desc *io_desc, int nr)
    {
    	struct map_desc *md;
    	struct vm_struct *vm;
    	struct static_vm *svm;
    
    	if (!nr)
    		return;
    
    	svm = early_alloc_aligned(sizeof(*svm) * nr, __alignof__(*svm));
    
    	for (md = io_desc; nr; md++, nr--) {
    		create_mapping(md);
    
    		vm = &svm->vm;
    		vm->addr = (void *)(md->virtual & PAGE_MASK);
    		vm->size = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));
    		vm->phys_addr = __pfn_to_phys(md->pfn);
    		vm->flags = VM_IOREMAP | VM_ARM_STATIC_MAPPING;
    		vm->flags |= VM_ARM_MTYPE(md->type);
    		vm->caller = iotable_init;
    		add_static_vm_early(svm++);
    	}
    }

也是调用create_mapping依次对传入的io端口地址进行映射,而且利用add_static_vm_early函数添加到了vmlist和static_vmlist链表,这两个全局链表应该是用来管理虚拟内存的,标记io映射使用的虚拟内存。具体的后面再分析CMA与伙伴系统的关系的时候,再深入分析之间的联系。
总结下,我们主要是分析了dma_contiguous_remap的作用,主要是针对CMA的区域进行了重新的映射,主要是0x8c00 000 ~ 0xe000 0000这个区域清了之前低端内存的映射关系,又重新作了二级映射。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文