内存分配和管理

本文深入探讨Linux内核中内存管理的基础概念,包括页、区、高速缓存层以及内存分配函数的实现。重点阐述了如何在不同场景下选择合适的内存分配策略,以及高端内存的映射机制。通过理解这些核心组件,开发者能够更高效地进行驱动开发和内存管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 1.     分配内存的底层函数

1.1 页:

/*********************include/linux/mm_type.h**************************/

struct page {

unsigned long flags;   

atomic_t _count;              /* Usage count, see below. */

union {

           atomic_t _mapcount; 

           struct {               /* SLUB */

                    u16inuse;

                    u16 objects;

       };

};

union {

struct {

           unsigned long private; 

           structaddress_space *mapping;      

    };

#if USE_SPLIT_PTLOCKS

spinlock_tptl;

#endif

structkmem_cache *slab;        /* SLUB: Pointer to slab */

struct page *first_page;  /* Compound tail pages */

};

union {

           pgoff_t index;  

           void *freelist;  

};

structlist_headlru; 

#if defined(WANT_PAGE_VIRTUAL)

void *virtual;   

#endif /* WANT_PAGE_VIRTUAL */

#ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS

unsigned long debug_flags;     /* Use atomic bitops on this */

#endif

#ifdef CONFIG_KMEMCHECK

void *shadow;

#endif

};

/*********************include/linux/mm_type.h**************************/

Struct  page成员不需要我来说明,内核代码注释是最好的解释。为了方便的管理内存,系统为每个物理页都分配这样一个结构,在物理内存的某个区域实现地址映射。

页接口:/include/linux/gfp.h

struct page *alloc_pages(unsigned int flags, unsigned int order);

返回2^order个页面第一个页指针

unsigned long __get_free_pages(gfp_tgfp_mask, unsigned int order);

返回2^order个页面第一个页逻辑地址

Unsignedlong get_zeroed_page(gfp_tgfp_mask);

分配一页全清0的页

void free_pages(unsigned long addr, unsigned int order);

 

#define free_page(addr) free_pages((addr),0)

 

 

 

1.2    区

由于硬件上的一些限制和将相似的页可划分一类的页,系统将页划分为四类区:

1.       ZONE_DMA32:用于执行DMA的页

2.       ZONE_NORMAL:正常映射的页

3.       ZONE_HIGHMEM:无法永久映射的页

4.       ZONE_MOVABLE

类型

X86系统结构

ZONE_DMA32

<16M

ZONE_NORMAL

16~900M

ZONE_HIGHMEM

>896M

ZONE_MOVABLE

 

区的应用并没有什么物理意义,只是为了方便管理,下面是区的结构:

struct zone {

         unsigned long watermark[NR_WMARK];

         unsigned long            lowmem_reserve[MAX_NR_ZONES];

#ifdef CONFIG_NUMA

         int node;

         unsigned long            min_unmapped_pages;

         unsigned long            min_slab_pages;

#endif

         structper_cpu_pageset __percpu *pageset;

         spinlock_t                   lock;

         intall_unreclaimable; /* All pages pinned */

#ifdef CONFIG_MEMORY_HOTPLUG

         /* see spanned/present_pages for more description */

         seqlock_t          span_seqlock;

#endif

         structfree_area        free_area[MAX_ORDER];

 

#ifndef CONFIG_SPARSEMEM

         unsigned long            *pageblock_flags;

#endif /* CONFIG_SPARSEMEM */

ZONE_PADDING(_pad1_)

         spinlock_t                   lru_lock;  

         structzone_lru {

                   structlist_head list;

         } lru[NR_LRU_LISTS];

         structzone_reclaim_statreclaim_stat;

         unsigned long            pages_scanned;          /* since last reclaim */

         unsigned long            flags;                     /* zone flags, see below */

         /* Zone statistics */

         atomic_long_t          vm_stat[NR_VM_ZONE_STAT_ITEMS];

         intprev_priority;

         unsignedintinactive_ratio;

         ZONE_PADDING(_pad2_)

         wait_queue_head_t         * wait_table;

         unsigned long            wait_table_hash_nr_entries;

         unsigned long            wait_table_bits;

         structpglist_data     *zone_pgdat;

         /* zone_start_pfn == zone_start_paddr>> PAGE_SHIFT */

         unsigned long            zone_start_pfn;

         unsigned long            spanned_pages;       /* total size, including holes */

         unsigned long            present_pages;        /* amount of memory (excluding holes) */

         const char                  *name;

} ____cacheline_internodealigned_in_smp;

2 驱动中常用的内存分配函数

2.1  kmalloc

首先来看看kmalloc的实现:

/****************/include/linux/slub_def.h***********************/

static __always_inline void *kmalloc(size_t size, gfp_t flags)

{

         void *ret;

         if (__builtin_constant_p(size)) {

                   if (size > SLUB_MAX_SIZE)

                            returnkmalloc_large(size, flags);

                   if (!(flags & SLUB_DMA)) {

                            structkmem_cache *s = kmalloc_slab(size);

                            if (!s)

                                     return ZERO_SIZE_PTR;

                            ret = kmem_cache_alloc_notrace(s, flags);

                            trace_kmalloc(_THIS_IP_, ret, size, s->size, flags);

                             return ret;}

         }

         return __kmalloc(size, flags);

}

/****************/include/linux/slub_def.h***********************/

关于这段代码,调用其他函数不熟悉,现在可能还看不懂,放一放再说。关于函数的flags类型太多,详见参考手册。

【小知识:TLB是将虚拟地址到物理地址的缓存表硬件缓冲区,可极大提高系统系能】

 2.2  vmalloc

Kmalloc分配的页地址在物理上是连续的,在虚拟地址空间也是连续的。Vmalloc则不同,它仅仅保证在虚拟地址上是连续的。由于需要将物理上不连续的地址映射到虚拟地址是连续的,在分配小内存时候性能不行。常常用于大内存分配上,该函数同样可导致睡眠。

3 . slab层

用过缓冲池的都知道,内存的分配和释放是链表的操作过程。高速缓存通过slab层来管理对象,如下图:

 

相关的函数接口:

包含的头文件:#include <linux/malloc.h>

kmem_cache_t *kmem_cache_create(char *name, size_t size, size_t offset, unsigned long flags, constructor(), destructor( ));

创建一个高速缓存

intkmem_cache_destroy(kmem_cache_t *cache);

销毁一个高速缓存

void *kmem_cache_alloc(kmem_cache_t *cache, int flags);

从高速缓存中获取对象

void kmem_cache_free(kmem_cache_t *cache, const void *obj);

从高速缓存中释放对象

 

 

 

 

好了,slab层使用的情况已经很明显了,它用于大量频繁的操作某个对象的场合

 4.高端内存的映射

根据划分区的知识,对于物理内存高于896M的内存无法永久映射到地址空间去,对于低端内存则是正常映射的。【为什么呢????我推测的理由如下:

假如某个用户的内存大于2GB(现在市场上的电脑配置大都是2GB),而高端内存896~2GB被映射到地址空间3GB-4GB之间,由于高端物理内存大于内核地址空间(3GB-4GB),那么必然有部分内存无法映射,为了充分利用这个地址空间,最好就是动态映射。相应的函数如下:

Void *kmap(struct page *page);

将页永久映射到内核地址空间

Void kunmap(struct page *page);

解除映射【可导致睡眠】

Void *kmap_atomic(struct page*page,enumkm_type type);

将页临时映射到内核地址空间

Void knumap_atomic(void *kvaddr, enumkm_type type);

解除映射【不能导致睡眠】

 好了,内存分配大约就是这些,这些函数就够我们做驱动开发的内存分配了。关于内存分配的选择,这里总结下:getpages,kmalloc是大多数情况下的选择,注意kmalloc的标志不同,可选择该函数是否能睡眠。对应于进程上下文和中断上下文中。Vmalloc适用于物理内存不连续,虚拟地址连续的情况,因为中间有个映射,性能有所损失。Slab缓存适用于频繁的操作大量的对象。然后就是高端内存的映射,这个我们一般不会用到,了解即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值