堆内存(6)——MORECORE宏

MORECORE宏

MORECORE是一个宏定义,其最终是通过系统调用分配内存,定义在linux内核的mmap.c文件中,

SYSCALL_DEFINE1(brk, unsigned long, brk){
    unsigned long retval;
    unsigned long newbrk, oldbrk;
    struct mm_struct *mm = current->mm;
    unsigned long min_brk;
    bool populate;
    down_write(&mm->mmap_sem);
    min_brk = mm->start_brk;
    if (brk < min_brk)
        goto out;
    if (check_data_rlimit(rlimit(RLIMIT_DATA), brk, mm->start_brk,
                  mm->end_data, mm->start_data))
        goto out;
    newbrk = PAGE_ALIGN(brk);
    oldbrk = PAGE_ALIGN(mm->brk);
    if (oldbrk == newbrk)
        goto set_brk;
    if (brk <= mm->brk) {
        if (!do_munmap(mm, newbrk, oldbrk-newbrk))
            goto set_brk;
        goto out;
    }
    if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
        goto out;
    if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
        goto out;
set_brk:
    mm->brk = brk;
    populate = newbrk > oldbrk && (mm->def_flags & VM_LOCKED) != 0;
    up_write(&mm->mmap_sem);
    if (populate)
        mm_populate(oldbrk, newbrk - oldbrk);
    return brk;
out:
    retval = mm->brk;
    up_write(&mm->mmap_sem);
    return retval;
}

首先会对传入堆的新地址brk做一些检查,然后该新地址小于原本的brk,就需要通过do_munmap释放虚拟内存,以减少堆的大小;反之,就通过do_brk增加堆得大小。其中find_vma_intersection用来判断增加堆空间后,是否会占用已经被分配的虚拟内存,

static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr){
    struct vm_area_struct * vma = find_vma(mm,start_addr);
    if (vma && end_addr <= vma->vm_start)
        vma = NULL;
    return vma;
}

因为是增加堆的大小,因此只需要关心do_brk函数,

static unsigned long do_brk(unsigned long addr, unsigned long len){
    struct mm_struct *mm = current->mm;
    struct vm_area_struct *vma, *prev;
    unsigned long flags;
    struct rb_node **rb_link, *rb_parent;
    pgoff_t pgoff = addr >> PAGE_SHIFT;
    int error;
    len = PAGE_ALIGN(len);
    if (!len)
        return addr;
    flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
    error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
    if (error & ~PAGE_MASK)
        return error;
    error = mlock_future_check(mm, mm->def_flags, len);
    if (error)
        return error;
    verify_mm_writelocked(mm);
    while (find_vma_links(mm, addr, addr + len, &prev, &rb_link,
                  &rb_parent)) {
        if (do_munmap(mm, addr, len))
            return -ENOMEM;
    }
    if (!may_expand_vm(mm, len >> PAGE_SHIFT))
        return -ENOMEM;
    if (mm->map_count > sysctl_max_map_count)
        return -ENOMEM;
    if (security_vm_enough_memory_mm(mm, len >> PAGE_SHIFT))
        return -ENOMEM;
    vma = vma_merge(mm, prev, addr, addr + len, flags,
                    NULL, NULL, pgoff, NULL);
    if (vma)
        goto out;
    vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
    if (!vma) {
        vm_unacct_memory(len >> PAGE_SHIFT);
        return -ENOMEM;
    }
    INIT_LIST_HEAD(&vma->anon_vma_chain);
    vma->vm_mm = mm;
    vma->vm_start = addr;
    vma->vm_end = addr + len;
    vma->vm_pgoff = pgoff;
    vma->vm_flags = flags;
    vma->vm_page_prot = vm_get_page_prot(flags);
    vma_link(mm, vma, prev, rb_link, rb_parent);
out:
    perf_event_mmap(vma);
    mm->total_vm += len >> PAGE_SHIFT;
    if (flags & VM_LOCKED)
        mm->locked_vm += (len >> PAGE_SHIFT);
    vma->vm_flags |= VM_SOFTDIRTY;
    return addr;
}

get_unmapped_area用来检查需要分配的虚拟内存地址是否已经被使用,find_vma_links用来查找需要插入的虚拟内存在红黑树的位置,may_expand_vm用来检查虚拟内存是否会超过系统的限制,vma_merge用来合并虚拟内存,如果不能合并,就通过slab分配一个vma,进行相应的设置,并通过vma_link插入到进程的红黑树中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值