Linux内存细节 vmalloc_init 及 ioremap - linux内存管理(六)

是不是我错了,本想这个函数会如网上所说将进行非连续内存管理的初始化,但是对于2.6.34的ARM架构而言,该函数实际完成的业务非常少。

内存管理的初始化读到此处,我感觉原有的认识存在很大缺陷:

(1)内核空间的下限是3G吗?永久映射的PKMAP_BASE已在3G下;

(2)低端内存是896M吗?2.6.32的omap4430的VMLLOC_END是1G - 128M,VMALLOC_MIN是1G - 128M -128M;

(3)还存在固定映射吗?FIXADDR_SIZE的空间已被FIX_KMAP_BEGIN ~ FIXK_KMAP_END完全占据;

(4)I/O空间在初始化已固定映射至VMALLOC_END到FIXADDR_START之间的一块虚拟空间区域.

(5)用户态不可以执行3G以上空间代码吗?kuser_cmpxchg_check检测的意义;

(6)引入MODULES_END的意义是什么?

对于ARM架构,vmalloc_init中的for循环就没有执行,如有误,望指正.

void vmalloc_init(void)
  |-->for_each_possible_cpu(i)
  |--{
  |    struct vmap_block_queue *vbq;
  |    vbq = &per_cpu(vmap_block_queue, i);
  |    spin_lock_init(&vbp->lock);
  |    INIT_LIST_HEAD(&vbq->free);
  |--}
  |
  |--for(tmp = vmlist; tmp; tmp = tmp->next)
  |--{
  |    xxxxxxxx
  |    对于2.6.34的ARM架构而言,vmlist直到此时仍为0;
  |    所以该循环不会执行,至于网上的资料大概是针对X86的.
  |--}
  |
  |--vmap_area_pcpu_hole = VMALLOC_END;
  |  vmap_initialized = true; 

如下部分是我后期再看init_arch_irq()中看的,主要是因为我们知道:对于ARM架构, vmalloc_init中的for循环没有执行,那么我们自然会想何时对首次修改vmlist以及如何修改.

init_arch_irq中会执行ioremap函数,以下记录该函数的执行流程.

#define ioremap(cookie, size) \
    __arm_ioremap(cookie, size, MT_DEVICE)

void *__arm_ioremap(unsigned long phys_addr, size_t size,
                    unsigned int mtype)
  |-->__arm_ioremap_caller(phys_addr, size, mtype, NULL)

void *__arm_ioremap_caller(unsigned long phys_addr, size_t size,
        unsigned int mtype, void *caller)
  |-->unsigned long offset = phys_addr & ~PAGE_MASK;
  |   对于SOC,例如sep612,每个IP模块的所占空间都是4K的整数倍,所以
  |   一般情况下offset = 0;
  |
  |-->unsigned long pfn = __phys_to_pfn(phys_addr);
  |
  |-->return __arm_ioremap_pfn_caller(pfn, offset, size, mtype, caller);
void *__arm_ioremap_pfn_caller(unsigned long pfn, unsigned long offset,
                         size_t size, unsigned int mtype, void *caller)
  |-->const struct mem_type *type = get_mem_type(mtype)
  |   mem_type中存放页表映射属性,及页表的级数
  |
  |-->size = PAGE_ALIGN(offset + size);
  |
  |-->struct vm_struct *area = get_vm_area_caller(size, VM_IOREMAP,
  |                                                caller);
  |   根据size,在VMALLOC_START ~ VMALLOC_END中申请一块size + PAGE_SIZE
  |   大小的虚拟空间,由于非连续内存区域的管理还利用了红黑树,因此在获得vm_struct
  |   实例的同时,也将申请vmap_area实例,将申请的虚拟空间,纳入红黑树
  |   vmap_area_root.rb_node的管理.
  |
  |-->unsigned long addr = (unsigned long)area->addr;
  |
  |-->remap_area_pages(addr, pfn, size, type);
  |   前面讲过,申请的虚拟空间是size + PAGE_SIZE,此处我们看到,
  |   映射的物理空间大小是size.
  |
  |-->flush_cache_vmap(addr, addr+size);
  |
  |-->return (void *) (offset + addr);

struct vm_struct *get_vm_area_caller(unsigned long size,
                      unsigned long flags, void *caller)
  |-->return __get_vm_area_node(size, 1, flags, VMALLOC_START, 
          VMALLOC_END, -1, GFP_KERNEL, caller);


struct vm_struct *__get_vm_area_node(unsigned long size,
               unsigned long align, unsigned long flags,
                 unsigned long start, unsigned long end,
                 int node, gfp_t gfp_mask, void *caller)
  |-->struct vmap_area *va = NULL;
  |   struct vm_struct *area = NULL;
  |
  |-->if(flags & VM_IOREMAP)
  |--{
  |     int bit = fls(size);
  |     if(bit > IOREMAP_MAX_ORDER) bit = IOREMAP_MAX_ORDER;
  |     else if(bit < PAGE_SHIFT) bit = PAGE_SHIFT;
  |     align = 1 << bit;
  |--}
  |  对于IO 映射部分做了对其修正.
  |
  |
  |-->size = PAGE_ALIGN(size);
  |
  |-->area = kzalloc_node(sizeof(*area), gfp_mask & GPF_RECLAIM_MASK,
  |                        node);
  |   对于非连续内存区,既使用了vmlist这样的链表管理,也使用了vmap_area_root
  |   之类的红黑树进行管理,此处即申请需插入到vmlist链表中的vm_struct实例中.
  |
  |-->size += PAGE_SIZE;
  |   为了安全考虑,多申请了一页的虚拟空间(注意只是虚拟空间).
  |
  |-->va = alloc_vmap_area(size, align, start, end, node, gpf_mask);
  |   从虚拟内存start ~ end中申请一块size大小的虚拟空间,起止地址放在
  |   vmap_area实例中,并将该vmap_area实例插入到以vmap_area_root.rb_node
  |   为根的红黑树中.
  |
  |-->insert_vmalloc_vm(area, va, flags, caller);
  |   将vmap_area实例和vm_struct实例关联起来,并将vm_struct实例插入到
  |   vmlist链表中.

将vmap_area实例和vm_struct实例关联起来,并将vm_struct实例插入到vmlist链表中.
void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va)
  |-->struct vm_struct *tmp, **p;
  |
  |-->vm->flags = flags;
  |   vm->addr = (void *)va->va_start;
  |   vm->size = va->va_end - va->va_start;
  |   vm->caller = caller;
  |   va->private = vm;
  |   va->flags |= VM_VM_AREA;
  |
  |-->for(p = &vmlist; (tmp = *p) != NULL; p = &tmp->next)
  |--{
  |    if(tmp->addr >= vm->addr) break;
  |--}
  |
  |--vm->next = *p;
  |  *p = vm;

                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值