您的位置首页>企业动态>

Linux驱动技术之一:内存申请

导读大家好,我是极客范的本期栏目编辑小友,现在为大家讲解Linux驱动技术之一:内存申请问题。首先,下图展示了Linux的内存映射模型。每个进程

大家好,我是极客范的本期栏目编辑小友,现在为大家讲解Linux驱动技术之一:内存申请问题。

首先,下图展示了Linux的内存映射模型。

每个进程都有自己的进程空间,进程空间的0-3G为用户空间,3G-4G为内核空间。

每个进程的用户空间不在同一个物理内存页面,但是所有进程的内核空间对应同一个物理地址。

vmalloc分配的地址可以是高端内存,也可以是低端内存。

0-896MB的物理地址线性映射到物理映射区。

动态内存应用

和应用层一样,内核程序也需要动态分配内存。区别在于内核进程可以控制分配的内存是在用户空间还是内核空间。前者可用于将内存分配给用户空间的堆区域。例如,用户进程的用户空间malloc最终会通过系统调用回调内核空间的内存分配函数。此时,内存分配函数属于用户进程,可以将空间分配给用户进程的堆区并返回,最终使一个用户进程在自己的用户空间中获得内存分配。后者只在内核空间中分配,所以用户进程无法直接访问该空间,所以多用于满足内核程序本身的内存需求。以下是Linux内核空间申请内存的常用API:

kmalloc - kfree

kmalloc应用的内存在物理内存中是连续的,它们与实际物理地址之间只有固定的偏移量,因此存在简单的转换关系。该API主要用于应用小于页面大小的内存。kmalloc的底层需要调用__get_free_pages。gtp_t标志表示参数中的内存类型,是这个函数的缩写。常用的内存类型有GFP _ user、GFP _ kernel和GFP _ atomic。

GFP_USER意味着为用户空间页面分配内存,可以阻塞;

GFP_KERNEL是最常用的标志。注意,使用此标志申请内存时,如果暂时不能满足,会造成进程阻塞。所以,不要在非进程上下文中使用GFP_KERNEL,比如中断处理程序、小任务和内核定时器!

GFP_ATOMIC可用于上述三种情况。该标志意味着,如果请求的内存不可用,将立即返回。

/** * kmalloc -分配内存* @size:需要多少字节的内存。* @ flags:要分配的内存类型。@ flags参数可能是: * %GFP_USER -代表用户分配内存之一。愿你安睡。* %GFP_KERNEL -分配普通内核ram。愿你安睡。* %GFP_ATOMIC -分配不会休眠。可以使用应急池。* *例如,在中断处理程序中使用这个。*/void *kmalloc(size_t size,gfp_t标志);/** * kfree -释放kmalloc返回的先前分配的内存* @objp:指针。*如果@objp为空,则不执行任何操作。*/void kfree(const void * objp);

同一系列的应用编程接口也有

void *kzalloc(size_t大小,gfp_t标志)

_ _ get _ free _ page-free _ page

__get_free_pages(),与kmalloc()一样,是物理上连续的内存。这一系列函数是Linux内核中获取空闲内存的最低方法。因为底部伙伴算法按(2 n ) page _ size管理内存,所以它们总是按页分配内存。

无符号长__get_free_pages(gfp_t

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

同系列API还有

unsigned long __get_free_page(gfp_t gfp) unsigned long get_zeroed_page(gfp_t gfp_mask) struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)

void free_page(unsigned long addr)

vmalloc - vfree

vmalloc在虚拟内存空间给出一块连续的内存区,实质上,这片连续的虚拟内存在物理内存中并不一定连续,所以vmalloc申请的虚拟内存和物理内存之间也就没有简单的换算关系,正因如此,vmalloc()通常用于分配远大于__get_free_pages()的内存空间,它的实现需要建立新的页表,此外还会调用使用GFP_KERN的kmalloc,so,一定不要在中断处理函数,tasklet和内核定时器等非进程上下文中使用vmalloc!

/** * vmalloc - allocate virtually conTIguous memory * @size: allocaTIon size * Allocate enough pages to cover @size from the page level allocator and map them into conTIguous kernel virtual space. */void *vmalloc(unsigned long size) /** * vfree - release memory allocated by vmalloc() * @addr: memory base address */void vfree(const void *addr)

同系列的API还有

/** * vmalloc_32 - allocate virtually contiguous memory (32bit addressable) * @size: allocation size * Allocate enough 32bit PA addressable pages to cover @size from the page level allocator and map them into contiguous kernel virtual space. */void *vmalloc_32(unsigned long size)

slab缓存

我们知道,页是内存映射的基本单位,但内核中很多频繁创建的对象所需内存都不到一页,此时如果仍然按照页映射的方式,频繁的进行分配和释放就会造成资源的浪费,同时也会降低系统性能。为了解决的这样的问题,内核引入了slab机制,使对象在前后两次被使用时被分配在同一块内存或同一类内存空间,且保留了基本的数据结构,就可以大大提高效率。kmalloc的底层即是使用slab算法管理分配的内存的。注意,slab依然是以页为单位进行映射,只是映射之后分割这些页为相同的更小的单元,从而节省了内存。slab分配的单元不能小于32B或大于128K。

/** * kmem_cache_create - 创建slab缓存对象 * @name:slab缓存区名字, * @size:slab分配的缓存区的每一个单元的大小 * @align:缓存区内存的对齐方式,一般给0 * @flags:控制分配的位掩码, * %SLAB_POISON - Poison the slab with a known test pattern (a5a5a5a5) to catch references to uninitialised memory. * %SLAB_RED_ZONE - Insert `Red' zones around the allocated memory to check for buffer overruns. * %SLAB_HWCACHE_ALIGN - Align the objects in this cache to a hardware cacheline. This can be beneficial if you're counting cycles as closely as davem. * %SLAB_CACHE_DMA - Use GFP_DMA memory * %SLAB_STORE_USER - Store the last owner for bug hunting *define SLAB_PANIC - Panic if kmem_cache_create() fails */struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,unsigned long flags, void (*ctor)(void *))/** * kmem_cache_alloc - Allocate an object from this cache. * @cachep: The cache to allocate from. * @flags: See kmalloc(). * The flags are only relevant if the cache has no available objects. */void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) /** * kmem_cache_free - Deallocate an object * @cachep: The cache the allocation was from. * @objp: The previously allocated object. * Free an object which was previously allocated from this cache. */void kmem_cache_free(struct kmem_cache *cachep, void *objp) void kmem_cache_destroy(struct kmem_cache *s)

范例

//创建slab对象struct kmem_cache_t *xj_sbcache;xj_sbcache = kmem_cache_create("xjslab",sizeof(struct xj_unit_t),0,SLAB_CACHE_DMA|SLAB_PANIC,NULL,NULL);//分配slab缓存struct xj_unit_t *xj_unit;xj_unit = kmem_cache_alloc(xj_sbcache,GFP_KERNEL);kmem_cache_free(xj_sbcache, xj_unit);kmem_cache_destroy(xj_sbcache);

内存池

除了slab机制,内核还提供了传统的内存池机制来管理小块内存的分配。内存池主要是用来解决可能出现的内存不足的情况,因为一个内存池在创建的时候就已经分配好了一内存,当我们用mempool_alloc向一个已经创建好的内存池申请申请内存时,该函数首先会尝试回调内存池创建时的分配内存函数,如果已经没有内存可以分配,他就会使用内存池创建时预先分配的内存,这样就可以避免因为无内存分配而陷入休眠,当然,如果预分配的内存也已经使用完毕,还是会陷入休眠。slab机制的目的是提高内存使用率以及内存管理效率,内存池的目的是避免内存的分配失败。下面是内核中提供的关于内存池的API

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。