Heim >Betrieb und Instandhaltung >Betrieb und Wartung von Linux >Mehrere Funktionen im Zusammenhang mit der Linux-Speicherverwaltung

Mehrere Funktionen im Zusammenhang mit der Linux-Speicherverwaltung

青灯夜游
青灯夜游Original
2023-04-10 16:55:021339Durchsuche

Linux-Speicherverwaltungsfunktionen: 1. kmalloc(), wird für die Speicherzuweisung im Kernel-Status verwendet; 2. vmalloc(), wird im Allgemeinen nur in Software verwendet (keine entsprechende Hardware-Bedeutung), um Speicher von einem größeren zuzuweisen sequentieller Puffer; 3. Die Funktionen alloc_page() und alloc_pages() können im Kernelbereich zugewiesen werden. 4. Die Funktionsreihen __get_free_pages() geben die virtuelle Adresse einer oder mehrerer Seiten zurück. 5. kmem_cache_alloc()wait.

Mehrere Funktionen im Zusammenhang mit der Linux-Speicherverwaltung

Die Betriebsumgebung dieses Tutorials: Linux7.3-System, Dell G3-Computer.

In diesem Artikel werden mehrere gängige Speicherzuweisungsfunktionen im Linux-Kernel sowie deren Gemeinsamkeiten und Unterschiede beschrieben, um ein besseres Verständnis des zugrunde liegenden Speicherzuweisungsmechanismus von Linux zu erhalten.

1.kmalloc()

kmalloc()-Funktion ähnelt unserer allgemeinen malloc()-Funktion Ersteres wird für die Speicherzuweisung im Kernelmodus und Letzteres für den Benutzermodus verwendet.
Die Funktion kmalloc() weist einen kontinuierlichen Speicherplatz im physischen Speicher zu und löscht wie die Funktion malloc() nicht die darin enthaltenen Originaldaten Zuordnung Sehr schnell. Sein Prototyp lautet wie folgt:

static inline void *kmalloc(size_t size, gfp_t flags);	/*返回的是虚拟地址*/
  • size: Die zuzuweisende Speichergröße. Aufgrund des Linux-Speicherverwaltungsmechanismus kann der Speicher nur entsprechend der Seitengröße zugewiesen werden (im Allgemeinen 4 KB für 32-Bit-Maschinen und 8 KB für 64-Bit-Maschinen). Dies führt dazu, dass das System immer noch zurückkehrt, wenn wir nur wenige Bytes benötigen Speicher ist offensichtlich äußerst verschwenderisch. Daher ist die Verarbeitungsmethode von kmalloc im Gegensatz zu malloc: Der Kernel weist zunächst eine Reihe von Speicherpools unterschiedlicher Größe zu (32 B, 64 B, 128 B, ..., 128 KB). Wenn Speicher zugewiesen werden muss, weist das System größere Speicherpools zu größer oder gleich Gibt den kleinsten Speicherpool an, der Speicher benötigt. Das heißt, der von kmalloc zugewiesene Speicher beträgt mindestens 32 Byte und maximal 128 KB. Wenn die Größe 128 KB überschreitet, müssen Sie andere Speicherzuweisungsfunktionen ausprobieren, z. B. vmalloc().
  • flag: Dieser Parameter wird verwendet, um das Verhalten der Funktion zu steuern. Der am häufigsten verwendete Parameter ist GFP_KERNEL. Dies bedeutet, dass, wenn derzeit nicht genügend Speicher zugewiesen ist, Der Prozess geht in den Ruhezustand und das System puffert ihn. Nachdem der Inhalt des Bereichs auf die Festplatte übertragen wurde, wird genügend Speicher bereitgestellt, bevor der Prozess aktiviert und zugewiesen wird. Weitere Flags finden Sie im Bild unten:
    Mehrere Funktionen im Zusammenhang mit der Linux-Speicherverwaltung
  • Wenn Sie das GFP_ KERNEL-Flag zum Beantragen von Speicher verwenden und es vorübergehend nicht erfüllt werden kann, wird der Prozess in den Ruhezustand versetzt und auf das warten Seite, was zu einer Blockierung führt, sodass GFP_KERNE nicht zum Anfordern von Speicher verwendet werden kann, wenn der Kontext unterbrochen oder eine Spin-Sperre gehalten wird. Daher kann es nicht in Nicht-Prozesskontexten wie Interrupt-Handlern, Tasklets und Kernel-Timern blockieren. In diesem Fall sollte der Treiber das GFP_ATOMIC-Flag verwenden, um Speicher zu beantragen. Wenn Sie das GFP_ATOMIC-Flag zum Beantragen von Speicher verwenden und keine freie Seite vorhanden ist, wird diese ohne Wartezeit direkt zurückgegeben.
  • Zusätzlich zu den in der obigen Tabelle aufgeführten Flags enthält es auch Folgendes :
  • _ _GFP_DMA (muss in a zugewiesen werden). DMA-fähiger Speicherbereich)#🎜 🎜#
  • _ _GFP_HIGHMEM (Gibt an, dass sich der zugewiesene Speicher im High-Speicher befinden kann)
  • _ _GFP_COLD (Fordert eine Seite an, auf die eine Zeit lang nicht zugegriffen wird lange Zeit)
  • _ _GFP_NOWARN( Verhindert, dass der Kernel eine Warnung ausgibt, wenn eine Zuweisung nicht gefüllt werden kann)
  • _ _GFP_HIGH (Anfrage mit hoher Priorität, ermöglicht den Erhalt der letzten Speicherseite vom Kernel für den Notfall reserviert)
  • #🎜🎜 #_ _GFP_REPEAT (versuchen Sie Ihr Bestes, es erneut zu versuchen, wenn die Zuweisung fehlschlägt)
  • _ _GFP_NOFAIL (Das Flag kann nur erfolgreich angewendet werden, nicht empfohlen)
  • _ _GFP_NORETRY (wenn die Anwendung nicht angewendet werden kann, geben Sie sofort auf)
  • Der mit kmalloc() angeforderte Speicher sollte mit kfree() Die Verwendung dieser Funktion ähnelt free() im Benutzerbereich.
  • kfree()释放,这个函数的用法和用户空间的 free()类似。
  • 2、vmalloc()

    vmalloc()一般用在为只存在于软件中(没有对应的硬件意义)的较大的顺序缓冲区分配内存,当内存没有足够大的连续物理空间可以分配时,可以用该函数来分配虚拟地址连续但物理地址不连续的内存。由于需要建立新的页表,所以它的开销要远远大于kmalloc及后面将要讲到的__get_free_pages()函数。且vmalloc()不能用在原子上下文中,因为它的内部实现使用了标志为 GFP_KERNEL 的kmalloc()

    2, vmalloc()#🎜🎜##🎜🎜##🎜🎜#vmalloc( ) wird im Allgemeinen verwendet, um Speicher für größere sequentielle Puffer zuzuweisen, die nur in der Software vorhanden sind (keine entsprechende Hardware-Bedeutung). Wenn der Speicher nicht groß genug ist, um ihn zuzuweisen, kann diese Funktion zum Zuweisen von Speicher verwendet werden mit kontinuierlichen virtuellen Adressen, aber #🎜🎜# diskontinuierlichen physischen Adressen #🎜🎜#. Da eine neue Seitentabelle erstellt werden muss, ist ihr Overhead viel größer als bei kmalloc und der Funktion __get_free_pages(), die später besprochen wird. Und vmalloc() kann nicht in einem atomaren Kontext verwendet werden, da seine interne Implementierung kmalloc() mit dem Flag GFP_KERNEL verwendet. Der Funktionsprototyp lautet wie folgt: #🎜🎜#
    void *vmalloc(unsigned long size);
    void vfree(const void *addr);
    • 使用 vmalloc 函数的一个例子函数是 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;
    }

    3、页分配函数

    在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);
    }

    Mehrere Funktionen im Zusammenhang mit der Linux-Speicherverwaltung

    根据返回页面数目分类,分为仅返回单页面的函数和返回多页面的函数。

    3.1 alloc_page()和alloc_pages()函数

    该函数定义在头文件/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));
    }

    3.2 __get_free_pages()系列函数

    它是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()函数完成内存释放的。

    4、slab缓存

    • 当在驱动程序中,遇到反复分配、释放同一大小的内存块时(例如,inode、task_struct等),建议使用内存池技术(对象在前后两次被使用时均分配在同一块内存或同一类内存空间,且保留了基本的数据结构,这大大提高了效率)。在linux中,有一个叫做slab分配器的内存池管理技术,内存池使用的内存区叫做后备高速缓存。
    • salb相关头文件在linux/slab.h中,在使用后备高速缓存前,需要创建一个kmem_cache的结构体。

    4.1 创建slab缓存区

    该函数创建一个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:是可选的内存对象块析构函数(释放函数)。

    4.2 分配slab缓存函数

    一旦创建完后备高速缓冲区后,就可以调用kmem_cache_alloc()在缓存区分配一个内存块对象了,其原型如下:

    void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);

    cachep指向开始分配的后备高速缓存,flags与传给kmalloc函数的参数相同,一般为GFP_KERNEL。

    4.3 释放slab缓存

    该函数释放一个内存块对象:

    void *kmem_cache_free(struct kmem_cache *cachep, void *objp);

    4.4 销毁slab缓存

    kmem_cache_create对应的是销毁函数,释放一个后备高速缓存:

    int kmem_cache_destroy(struct kmem_cache *cachep);

    它必须等待所有已经分配的内存块对象被释放后才能释放后备高速缓存区。

    4.5 slab缓存使用举例

    创建一个存放线程结构体(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);

    5、内存池

    在 Linux 内核中还包含对内存池的支持,内存池技术也是一种非常经典的用于分配大量小对象的后备缓存技术。

    5.1 创建内存池

    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 标记被指定时,分配函数才会休眠。

    5.2 分配和回收对象

    在内存池中分配和回收对象需由以下函数来完成:

    void *mempool_alloc(mempool_t *pool, int gfp_mask); 
    void mempool_free(void *element, mempool_t *pool);

    mempool_alloc()用来分配对象,如果内存池分配器无法提供内存,那么就可以用预分配的池。

    5.3 销毁内存池

    void mempool_destroy(mempool_t *pool);

    mempool_create()函数创建的内存池需由 mempool_destroy()来回收。

    相关推荐:《Linux视频教程

    Das obige ist der detaillierte Inhalt vonMehrere Funktionen im Zusammenhang mit der Linux-Speicherverwaltung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn