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成员不需要我来说明,内核代码注释是最好的解释。为了方便的管理内存,系统为每个物理页都分配这样一个结构,在物理内存的某个区域实现地址映射。
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缓存适用于频繁的操作大量的对象。然后就是高端内存的映射,这个我们一般不会用到,了解即可。