当前位置: 首页 > 科技观察

Linux驱动技术(一)_内存申请_0

时间:2023-03-12 10:15:17 科技观察

先从基础开始吧。下图展示了Linux的内存映射模型。每个进程都有自己的进程空间。进程空间的0-3G是用户空间,3G-4G是内核空间。每个进程的用户空间不在同一个物理内存页,而是所有进程的内核空间对应同一个物理地址。vmalloc分配的地址可以是高端内存也可以是低端内存。0-896MB的物理地址线性映射到物理内存。的映射区域。内存动态申请与应用层相同。内核程序也需要动态分配内存。不同的是内核进程可以控制分配的内存是在用户空间还是内核空间。前者可以用来分配内存到用户空间的堆区,例如,用户进程的用户空间的malloc最终会通过系统调用回调内核空间的内存分配函数。此时内存分配函数属于用户进程,可以为用户进程的堆区分配空间并返回,最终使得用户进程在自己的用户空间中获得内存分配;后者只分配在内核空间,用户进程不能直接访问这块空间,所以多用于满足内核程序自身的内存需求。以下是Linux内核空间申请内存的常用API:kmalloc——kfreekmalloc申请的内存在物理内存中是连续的,它们与真实的物理地址只有固定的偏移量,因此存在简单的转换关系。该接口多用于申请小于页大小的内存。kmalloc底层需要调用__get_free_pages。参数中表示内存类型的gtp_tflags是该函数的简称。常用的内存类型有GFP_USER、GFP_KERNEL和GFP_ATOMIC。GFP_USER表示为用户空间页分配内存,可以阻塞;GFP_KERNEL是最常用的标志。注意,使用这个标志申请内存时,如果暂时不能满足,会造成进程阻塞。所以,一定不要在内核定时器等非进程上下文中使用中断处理函数、tasklets和UsingGFP_KERNEL!!!以上三种情况都可以使用GFP_ATOMIC。这个标志的意思是如果请求的内存不能使用,它会立即返回。/***kmalloc-allocatememory*@size:howmanybytesofmemoryarerequired.*@flags:thetypeofmemorytoallocate.*The@flagsargumentmaybeoneof:*%GFP_USER-Allocatememoryonbehalfofuser.Maysleep.*%GFP_KERpoNEL-Allocatenormalkernelram.Maysleep.*%GFP_ATOMICIL-Allocate**Forexample,usethisinsideinterrupthandlers.*/void*kmalloc(size_tsize,gfp_tflags);/***kfree-freepreviouslyallocatedmemory*@objp:pointerreturnedbykmalloc.*If@objpisNULL,nooperationisperformed.*/voidkfree(constvoid*objp);ThesameseriesAPIalsohasvoid*kzalloc(size_tsize,gfp_tflags)__get_free_pages-free_pages__get_free_pages()isphysicallycontiguousmemorylikekmalloc().ThisseriesoffunctionsisthemethodusedtoobtainfreememoryatthehighestlevelintheLinuxkernel,becausetheunderlyingbuddyalgorithmis是以(2^n)×PAGE_SIZE来管理内存的,所以他们总是以页为单位分配内存的unsignedlong__get_free_pages(gfp_tgfp_mask,unsignedintorder)voidfree_pages(unsignedlongaddr,unsignedintorder)同系列API还有unsignedlong__get_free_page(gfp_tgfp)unsignedlongget_zeroed_page(gfp_tgfp_mask)structpage*alloc_pages(gfp_tgfp_mask,unsignedinterder)voidfree_page(unsignedlongaddr)vmalloc-vfreevmalloc在虚拟内存空间中给出一个连续的内存区域。从本质上讲,这个连续的虚拟内存在物理内存中不一定是连续的,所以vmalloc申请的虚拟内存和物理内存也不存在简单的转换关系。为此,通常使用vmalloc()来分配比__get_free_pages()大得多的内存空间。它的实现需要创建一个新的页表。此外,它还会使用GFP_KERN调用kmalloc,因此,一定不要在中断处理程序、tasklet和内核定时器等非进程上下文中使用vmalloc!()*@addr:memorybaseaddress*/voidvfree(constvoid*addr)同系列的API还有/***vmalloc_32-allocatevirtuallycontiguousmemory(32bitaddressable)*@size:allocationsize*Allocateenough32bitPAaddressablepagestocover@sizefromthepagelevelallocatorandmapthemintocontiguouskernelvirtualspace*/volong3id2(Cache我们知道页面是内存映射的基本单位,但是内核中很多频繁创建的对象所需要的内存都不到一页。这时候如果仍然使用页面映射的方式,频繁的分配和释放会造成资源消耗。浪费,而且还会降低系统性能为了解决此类问题,内核引入了slab机制,使得对象在被使用两次时分配在同一块内存或同一类型的内存空间中,而基础数据结构得以保留。效率可以大大提高。kmalloc底层使用slab算法管理分配的内存。注意slab还是以page为单位进行映射,只是这些page在映射后被划分为相同的更小的单元,从而节省了内存。slab分配的单元不能小于32B,也不能大于128K。/***kmem_cache_create-创建slab缓存对象*@name:slab缓存名称,*@size:slab分配的缓存中每个单元的大小*@align:缓存内存的对齐方式,一般为0*@flags:控制分配的位掩码,*%SLAB_POISON-Poisontheslabwithaknowntestpattern(a5a5a5a5)tocatchreferencestouninitialisedmemory.*%SLAB_RED_ZONE-Insert`Red'zonesaroundtheallocatedmemorytocheckforbufferoverruns.*%SLAB_HWCACHE_ALIGN-Aligntheobjectsinthiscachetoahardwarecacheline.Thiscanbebeneficialifyou'recountingcyclesascloselyasdavem.*%SLAB_CACHE_DMA-UseGFP_DMAmemory*%SLAB_STORE_USER-Storethelastownerforbughunting*defineSLAB_PANIC-Panicifkmem_cache_create()失败*/structkmem_cache*kmem_cache_create(constchar*name,size_tsize,size_talign,unsignedlongflags,void(*ctor)(void*))/***kmem_cache_alloc-Allocateanobjectfromthiscache.*@cachep:Thecachetoallocateflagfroms:.查找malloc()。*如果缓存没有可用对象,则标志仅相关。*/void*kmem_cache_alloc(structkmem_cache*cachep,gfp_tflags)/***kmem_cache_free-Deallocateanobject*@cachep:Thecachetheallocationwasfrom.*@objp:Thepreviouslyallocatedobject.*Freeanobjectwhichwaspreviouslyallocatedfromthiscache.*/voidkmem_cache_free(structkmem_cache*cachep,void*objp)voidkmem_cache_destroy(structkmem_cache*s)范例//创建slab对象structkmem_cache_t*xj_sbcache;xj_sbcache=kmem_cache_create("xjslab",sizeof(structxj_unit_t),0,SLAB_CACHE_DMA|SLAB_PANIC,NULL,NULL);//分配slab缓存structxj_unit_t*xj_unit;xj_unit=kmem_cache_alloc(xj_sbcache,GFP_KERNEL);/*使用slab缓存*//*释放slab缓存*/kmem_cache_free(xj_sbcache,xj_unit);/*销毁slab缓存*/kmem_cache_destroy(xj_sbcache);内存池除了slab机制外,内核还提供了传统的内存池机制来管理小块内存的分配。内存池主要是用来解决可能出现的内存不足的情况,因为一个内存池在创建的时候就已经分配了一块内存。当我们使用mempool_alloc向一个已经创建好的内存池申请内存时,该函数会先尝试回调内存池创建时的内存分配函数。如果没有内存,可以Allocation,他会在创建内存池的时候使用预先分配的内存,避免因为没有分配内存而陷入睡眠。当然,如果预分配的内存已经用完了,它还是会陷入休眠。slab机制的目的是提高内存使用和内存管理效率,内存池的目的是避免内存分配失败。下面是内核中提供的关于内部存储池的API/***mempool_create-createamemorypool*@min_nr:theminimumnumberofelementsguaranteedtobeallocatedforthispool.*@alloc_fn:user-definedelement-allocationfunction.*@free_fn:user-definedelement-freeingfunction.*@pool_data:optionalprivatedataavailabletotheuser-definedfunctions.**thisfunctioncreatesandallocatesaguaranteedsize,preallocatedmemorypool.Thepoolcanbeusedfromthemempool_alloc()andmempool_free()functions.*Thisfunctionmightsleep.Boththealloc_fn()andthefree_fn()functionsmightsleep-aslongasthemempool_alloc()functionisnotcalledfromIRQcontexts.*/mempool_t*mempool_create(intmin_nr,mempool_alloc_t*alloc_fn,mempool_free_t*free_fn,void*pool_data)/***mempool_alloc-从特定内存池分配元素*@pool:指向通过amempool_create()分配的内存池的指针。*@gfp_mask:通常的分配位掩码。*此函数仅在alloc_fn()函数中睡眠或返回NULL。请注意,由于重新分配,此函数永远不会*在从进程c调用时失败ontexts.(itmightfailifcalledfromanIRQcontext.)*/void*mempool_alloc(mempool_t*pool,gfp_tgfp_mask)/***mempool_free-returnanelementtothepool.*@element:poolelementpointer.*@pool:pointertothememorypoolwhichwasallocatedviamempool_create().**这个函数onlysleepsifthefree_fn()functionidsleempool_vopes.*/(void*element,mempool_t*pool)/***mempool_destroy-deallocateamemorypool*@pool:pointertothememorypoolwhichwasallocatedviamempool_create().**Freeallreservedelementsin@pooland@poolitself.Thisfunctiononlysleepsifthefree_fn()functionsleeps.*/voidmempool_destroy(mempool_t*pool)复制代码