常用内存分配函数kmalloc、vmalloc、malloc和mmap实现原理

1.kmalloc函数

static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
    if (__builtin_constant_p(size)) {
#ifndef CONFIG_SLOB
        unsigned int index;
#endif
        if (size > KMALLOC_MAX_CACHE_SIZE)
            return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
        index = kmalloc_index(size);  ///查找使用的哪个slab缓冲区
 
        if (!index)
            return ZERO_SIZE_PTR;
 
        return kmem_cache_alloc_trace(    ///从slab分配内存
                kmalloc_caches[kmalloc_type(flags)][index],
                flags, size);
#endif
    }
    return __kmalloc(size, flags);
}

kmem_cache_alloc_trace分配函数

void *
kmem_cache_alloc_trace(struct kmem_cache *cachep, gfp_t flags, size_t size)
{
    void *ret;
 
    ret = slab_alloc(cachep, flags, size, _RET_IP_);  ///分配slab缓存
 
    ret = kasan_kmalloc(cachep, ret, size, flags);
    trace_kmalloc(_RET_IP_, ret,
              size, cachep->size, flags);
    return ret;
}

可见,kmalloc()基于slab分配器实现,因此分配的内存,物理上都是连续的。

2.vmalloc函数

vmalloc()
    ->__vmalloc_node_flags()
    ->__vmalloc_node()
    ->__vmalloc_node_range()

核心函数__vmalloc_node_range

static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
                 pgprot_t prot, unsigned int page_shift,
                 int node)
{
    const gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
    unsigned long addr = (unsigned long)area->addr;
    unsigned long size = get_vm_area_size(area);   ///计算vm_struct包含多少个页面
    unsigned long array_size;
    unsigned int nr_small_pages = size >> PAGE_SHIFT;
    unsigned int page_order;
    struct page **pages;
    unsigned int i;
 
    array_size = (unsigned long)nr_small_pages * sizeof(struct page *);
    gfp_mask |= __GFP_NOWARN;
    if (!(gfp_mask & (GFP_DMA | GFP_DMA32)))
        gfp_mask |= __GFP_HIGHMEM;
 
    /* Please note that the recursion is strictly bounded. */
    if (array_size > PAGE_SIZE) {
        pages = __vmalloc_node(array_size, 1, nested_gfp, node,
                    area->caller);
    } else {
        pages = kmalloc_node(array_size, nested_gfp, node);
    }
 
    if (!pages) {
        free_vm_area(area);
        warn_alloc(gfp_mask, NULL,
               "vmalloc size %lu allocation failure: "
               "page array size %lu allocation failed",
               nr_small_pages * PAGE_SIZE, array_size);
        return NULL;
    }
 
    area->pages = pages;  ///保存已分配页面的page数据结构的指针
    area->nr_pages = nr_small_pages;
    set_vm_area_page_order(area, page_shift - PAGE_SHIFT);
 
    page_order = vm_area_page_order(area);
 
    /*
     * Careful, we allocate and map page_order pages, but tracking is done
     * per PAGE_SIZE page so as to keep the vm_struct APIs independent of
     * the physical/mapped size.
     */
    for (i = 0; i < area->nr_pages; i += 1U << page_order) {
        struct page *page;
        int p;
 
        /* Compound pages required for remap_vmalloc_page */
        page = alloc_pages_node(node, gfp_mask | __GFP_COMP, page_order); ///分配物理页面
        if (unlikely(!page)) {
            /* Successfully allocated i pages, free them in __vfree() */
            area->nr_pages = i;
            atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
            warn_alloc(gfp_mask, NULL,
                   "vmalloc size %lu allocation failure: "
                   "page order %u allocation failed",
                   area->nr_pages * PAGE_SIZE, page_order);
            goto fail;
        }
 
        for (p = 0; p < (1U << page_order); p++)
            area->pages[i + p] = page + p;
 
        if (gfpflags_allow_blocking(gfp_mask))
            cond_resched();
    }
    atomic_long_add(area->nr_pages, &nr_vmalloc_pages);
 
    if (vmap_pages_range(addr, addr + size, prot, pages, page_shift) < 0) { ///建立物理页面到vma的映射
        warn_alloc(gfp_mask, NULL,
               "vmalloc size %lu allocation failure: "
               "failed to map pages",
               area->nr_pages * PAGE_SIZE);
        goto fail;
    }
 
    return area->addr;
 
fail:
    __vfree(area->addr);
    return NULL;
}

可见,vmalloc是临时在vmalloc内存区申请vma,并且分配物理页面,建立映射;直接分配物理页面,至少一个页4K,因此vmalloc适合用于分配较大内存,并且物理内存不一定连续;

【文章福利】小编推荐自己的Linux内核技术交流群: 【977878001】整理一些个人觉得比较好得学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!前100进群领取,额外赠送一份 价值699的内核资料包(含视频教程、电子书、实战项目及代码)

内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

3.malloc函数

malloc是C库实现的函数,C库维护了一个缓存,当内存够用时,malloc直接从C库缓存分配,只有当C库缓存不够用࿱

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值