Linux メモリ管理関連関数: 1. kmalloc()、カーネル モードでのメモリ割り当てに使用; 2. vmalloc()、一般にソフトウェア内にのみ存在する関数に使用 (対応するハードウェアの意味はない) より大きなシーケンシャル バッファメモリの割り当て; 3. alloc_page() および alloc_pages() 関数はカーネル空間に割り当てることができます; 4. __get_free_pages() 一連の関数は 1 つ以上のページの仮想アドレスを返します; 5. kmem_cache_alloc() など。
#このチュートリアルの動作環境: linux7.3 システム、Dell G3 コンピューター。
この記事では、Linux の基礎となるメモリ割り当てメカニズムをより深く理解できるように、Linux カーネルのいくつかの一般的なメモリ割り当て関数とその類似点と相違点について説明します。
kmalloc() 関数は、一般的な malloc() 関数に似ています。前者はカーネル状態に使用されます。メモリ、割り当て、後者はユーザー モードで使用されます。
kmalloc() 関数は、物理メモリ内に 連続 ストレージ スペースを割り当てます。malloc() 関数と同様に、内部の元のデータはクリアされません。メモリが十分であれば、その割り当て速度は非常に速くなります。速い。 。そのプロトタイプは次のとおりです。
static inline void *kmalloc(size_t size, gfp_t flags); /*返回的是虚拟地址*/
kfree ()
Release を使用する必要があります。この関数の使用法は、ユーザー空間での free() と似ています。 vmalloc()
は通常、ソフトウェア内でのみ使用されます (対応するハードウェアの意味) は、より大きなシーケンシャル バッファにメモリを割り当てます。メモリに割り当てるのに十分な大きさの連続した物理スペースがない場合、この関数を使用して連続した仮想アドレスを割り当てることができますが、不連続な物理アドレス #メモリ。新しいページテーブルを作成する必要があるため、そのオーバーヘッドは kmalloc や、後で説明する __get_free_pages()
関数よりもはるかに大きくなります。また、vmalloc()
は、内部実装で GFP_KERNEL フラグを指定した kmalloc()
を使用するため、アトミック コンテキストでは使用できません。その関数プロトタイプは次のとおりです:
void *vmalloc(unsigned long size); void vfree(const void *addr);
create_module()
系统调用,它利用 vmalloc()函数来获取被创建模块需要的内存空间。copy_from_user()
对内存进行使用。下面举一个使用vmalloc函数的示例:static int xxx(...) { ... cpuid_entries = vmalloc(sizeof(struct kvm_cpuid_entry) * cpuid->nent); if(!cpuid_entries) goto out; if(copy_from_user(cpuid_entries, entries, cpuid->nent * sizeof(struct kvm_cpuid_entry))) goto out_free; for(i=0; i<cpuid->nent; i++){ vcpuid->arch.cpuid_entries[i].eax = cpuid_entries[i].eax; ... vcpuid->arch.cpuid_entries[i].index = 0; } ... out_free: vfree(cpuid_entries); out: return r; }
在linux中,内存分配是以页为单位的,32位机中一页为4KB,64位机中,一页为8KB,但具体还有根据平台而定。
根据返回值类型的不同,页分配函数分为两类,一是返回物理页地址,二是返回虚拟地址。虚拟地址和物理地址起始相差一个固定的偏移量。
#define __pa(x) ((x) - PAGE_OFFSET) static inline unsigned long virt_to_phys(volatile void *address) { return __pa((void *)address); } #define __va(x) ((x) + PAGE_OFFSET) static inline void* phys_to_virt(unsigned long address) { return __va(address); }
根据返回页面数目分类,分为仅返回单页面的函数和返回多页面的函数。
该函数定义在头文件/include/linux/gfp.h
中,它既可以在内核空间分配,也可以在用户空间分配,它返回分配的第一个页的描述符而非首地址,其原型为:
#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0) #define alloc_pages(gfp_mask, order) alloc_pages_node(numa_node_id(), gfp_mask, order) //分配连续2^order个页面 static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order) { if(unlikely(order >= MAX_ORDER)) return NULL; if(nid < 0) nid = numa_node_id(); return __alloc_pages(gfp_mask, order, noed_zonelist(nid, gfp_mask)); }
它是kmalloc函数实现的基础,返回一个或多个页面的虚拟地址。该系列函数/宏包括 get_zeroed_page()
、_ _get_free_page()
和_ _get_free_pages()
。在使用时,其申请标志的值及含义与 kmalloc()
完全一样,最常用的是 GFP_KERNEL 和 GFP_ATOMIC。
/*分配多个页并返回分配内存的首地址,分配的页数为2^order,分配的页不清零。 order 允许的最大值是 10(即 1024 页)或者 11(即 2048 页),依赖于具体 的硬件平台。*/ unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order) { struct page *page; page = alloc_pages(gfp_mask, order); if(!page) return 0; return (unsigned long)page_address(page); } #define __get_free_page(gfp_mask) __get_free_pages(gfp_mask, 0) /*该函数返回一个指向新页的指针并且将该页清零*/ unsigned long get_zeroed_page(unsigned int flags);
_ _get_free_pages()
系列函数/宏申请的内存应使用free_page(addr)
或free_pages(addr, order)
函数释放:#define __free_page(page) __free_pages((page), 0) #define free_page(addr) free_pages((addr), 0) void free_pages(unsigned long addr, unsigned int order) { if(addr != 0){ VM_BUG_ON(!virt_addr_valid((void*)addr)); __free_pages(virt_to_page((void *)addr), order); } } void __free_pages(struct page *page, unsigned int order) { if(put_page_testzero(page)){ if(order == 0) free_hot_page(page); else __free_pages_ok(page, order); } }
free_pages()函数是调用__free_pages()函数完成内存释放的。
kmem_cache
的结构体。该函数创建一个slab缓存(后备高速缓冲区),它是一个可以驻留任意数目全部同样大小的后备缓存。其原型如下:
struct kmem_cache *kmem_cache_create(const char *name, size_t size, \ size_t align, unsigned long flags,\ void (*ctor)(void *, struct kmem_cache *, unsigned long),\ void (*dtor)(void *, struct kmem_cache *, unsigned ong)));
其中:
name:创建的缓存名;
size:可容纳的缓存块个数;
align:后备高速缓冲区中第一个内存块的偏移量(一般置为0);
flags:控制如何进行分配的位掩码,包括 SLAB_NO_REAP
(即使内存紧缺也不自动收缩这块缓存)、SLAB_HWCACHE_ALIGN
( 每 个 数 据 对 象 被 对 齐 到 一 个 缓 存 行 )、SLAB_CACHE_DMA
(要求数据对象在 DMA 内存区分配)等);
ctor:是可选的内存块对象构造函数(初始化函数);
dtor:是可选的内存对象块析构函数(释放函数)。
一旦创建完后备高速缓冲区后,就可以调用kmem_cache_alloc()
在缓存区分配一个内存块对象了,其原型如下:
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);
cachep指向开始分配的后备高速缓存,flags与传给kmalloc函数的参数相同,一般为GFP_KERNEL。
该函数释放一个内存块对象:
void *kmem_cache_free(struct kmem_cache *cachep, void *objp);
与kmem_cache_create
对应的是销毁函数,释放一个后备高速缓存:
int kmem_cache_destroy(struct kmem_cache *cachep);
它必须等待所有已经分配的内存块对象被释放后才能释放后备高速缓存区。
创建一个存放线程结构体(struct thread_info)的后备高速缓存,因为在linux中涉及频繁的线程创建与释放,如果使用__get_free_page()函数会造成内存的大量浪费,效率也不高。所以在linux内核的初始化阶段就创建了一个名为thread_info的后备高速缓存,代码如下:
/* 创建slab缓存 */ static struct kmem_cache *thread_info_cache; thread_info_cache = kmem_cache_create("thread_info", sizeof(struct thread_info), \ SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); /* 分配slab缓存 */ struct thread_info *ti; ti = kmem_cache_alloc(thread_info_cache, GFP_KERNEL); /* 使用slab缓存 */ ... /* 释放slab缓存 */ kmem_cache_free(thread_info_cache, ti); kmem_cache_destroy(thread_info_cache);
在 Linux 内核中还包含对内存池的支持,内存池技术也是一种非常经典的用于分配大量小对象的后备缓存技术。
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, \ mempool_free_t *free_fn, void *pool_data);
mempool_create()函数用于创建一个内存池,min_nr 参数是需要预分配对象的数目,alloc_fn 和 free_fn 是指向内存池机制提供的标准对象分配和回收函数的指针,其原型分别为:
typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data); typedef void (mempool_free_t)(void *element, void *pool_data);
pool_data 是分配和回收函数用到的指针,gfp_mask 是分配标记。只有当_ _GFP_WAIT
标记被指定时,分配函数才会休眠。
在内存池中分配和回收对象需由以下函数来完成:
void *mempool_alloc(mempool_t *pool, int gfp_mask); void mempool_free(void *element, mempool_t *pool);
mempool_alloc()用来分配对象,如果内存池分配器无法提供内存,那么就可以用预分配的池。
void mempool_destroy(mempool_t *pool);
mempool_create()函数创建的内存池需由 mempool_destroy()来回收。
相关推荐:《Linux视频教程》
以上がLinuxのメモリ管理に関連するいくつかの関数の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。