`

Linux内核的物理内存管理

阅读更多

    在物理内容定义方面Linux引入了内存结节(node), 内存区(zone), 内存页page的概念。对物理内存的管理分两部分:最底层实现的页面级内存管理伙伴系统,基于伙伴系统实现的内核对象缓存和通用缓存Slab内存管理。

2,伙伴系统(Buddy System)

        节点:内核以struct pglist_data数据结构统一表示UMA系统和NUMA系统的内存结点,UMA只有一个内存节点,NUMA以链表的形式把内存节点串联起来。

    typedef struct pglist_data {
         struct zone node_zones[MAX_NR_ZONES];
         struct zonelist node_zonelists[GFP_ZONETYPES];
         int nr_zones;
         struct page *node_mem_map;
         struct bootmem_data *bdata;
         unsigned long node_start_pfn;
         unsigned long node_present_pages; /* total number of physical pages */
         unsigned long node_spanned_pages; /* total size of physical page range, including holes */
         int node_id;
         struct pglist_data *pgdat_next;
         wait_queue_head_t       kswapd_wait;
         struct task_struct *kswapd;
    } pg_data_t;

    内存区:Linux将节点的物理内存划分成不同的内存区,struct zone表示内存区,内存区的类型有:        

ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM 。

    struct zone {
        unsigned long  free_pages;
        unsigned long  pages_min, pages_low, pages_high;
        unsigned long  protection[MAX_NR_ZONES];

        struct per_cpu_pageset pageset[NR_CPUS];
        spinlock_t  lock;
        struct free_area free_area[MAX_ORDER];
        spinlock_t  lru_lock; 
        struct list_head active_list;
        struct list_head inactive_list;
        unsigned long  nr_scan_active;
        unsigned long  nr_scan_inactive;
        unsigned long  nr_active;
        unsigned long  nr_inactive;
        unsigned long  pages_scanned;    /* since last reclaim */
        int   all_unreclaimable; /* All pages pinned */
        wait_queue_head_t * wait_table;
        unsigned long  wait_table_size;
        unsigned long  wait_table_bits;
        struct pglist_data *zone_pgdat;
        struct page  *zone_mem_map;
        char   *name;
    }      

    struct free_area {
         struct list_head free_list[MIGRATE_TYPES];
         unsigned long  nr_free;
    };

    内存页:Linux内核为每一个物理内存页框创建一个struct page对象,系统用一个全局变量struct page* mem_page数组存放page对象。

    struct page {
        page_flags_t flags;  

        atomic_t _count;  /* Usage count, see below. */
        atomic_t _mapcount; 
        unsigned long private;  

        struct address_space *mapping;

        pgoff_t index;  

        struct list_head lru; 

       #if defined(WANT_PAGE_VIRTUAL)
        void *virtual;  

       #endif /* WANT_PAGE_VIRTUAL */
    };

    伙伴系统分配单个的物理页面或2的整数次幂个连续的物理页面。struct zone的成员free_area数组保存页面。阶是伙伴系统的一个非常重要的术语,它描述了内存分配的数量单位,内存块的长度为2的order次幂,order的范围从0到11。free_area[]数组以阶为索引,每个元素保存连续内存页的链表。第0个链表管理单页大小的内存块,第1个链表管理两个连续页大小的内存块,第3个链表管理4个连续页大小的内存块,以此类推。Linux采用著名的buddy System算法解决外部碎片问题,把所有的空闲页框分组成11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512,1024个连续的页框。对1024个页框的最大请求对应着4MB大小的连续RAM块。

    工作原理:

    假设要请求一个256个页框的块(1MB),算法先在256个页框的链表中检查是否有一个空闲块,如果没有,算法查找下一个更大的页块的链表,也就是在块大小为512个页的链表中找一个空闲块,如果存在,内核把该块分成两块,一半用于满足请求,一半插入到256个页框大小的块链表中。如果512个页的链表中也没有空闲块,继续查找更大的块链表,找到后,256个页用于满足请求,剩余的768个页框分两部分,512个页框和256个页的块,分别插入到相应的链表。释放过程则相反,内核试图把大小为b的块与链表中与释放块连续的块合并成大小为2b的单独块,插入到更大的块链表中。满足以下条件的两个块称为伙伴:

  •  两个块具有相同的大小,记作b
  •  它们的物理地址是连续的
  • 第一块的第一个页框的物理地址是2*b*(2的12次方)的位数。

    算法是迭代的,如果成功合并释放的块,它会试图合并2b的块,以再欠试图形成更大的块。

3,Buddy System API

    伙伴系统的API只分配2的整数幂个页

    alloc_pages(mask, order)分配2的order次幂个页,并返回一个struct page实例。

    get_zeroed_page(mask) 分配一个页,并返回 struct page实例,页对应的内容填充0.

    __get_free_pages和__get_free_page的工作方式与上述函数相同,但返回分配个内存块的虚拟地址。

    get_dma_pages(mask, order)用于获得适用于DMA的页

    内存分配标志mask:

    #define __GFP_WAIT ((__force gfp_t)___GFP_WAIT) /* Can wait and reschedule? */
    #define __GFP_HIGH ((__force gfp_t)___GFP_HIGH) /* Should access emergency pools? */
    #define __GFP_IO ((__force gfp_t)___GFP_IO) /* Can start physical IO? */
    #define __GFP_FS ((__force gfp_t)___GFP_FS) /* Can call down to low-level FS? */
    #define __GFP_COLD ((__force gfp_t)___GFP_COLD) /* Cache-cold page required */
    #define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN) 
    #define __GFP_REPEAT ((__force gfp_t)___GFP_REPEAT) /* See above */
    #define __GFP_NOFAIL ((__force gfp_t)___GFP_NOFAIL) /* See above */
    #define __GFP_NORETRY ((__force gfp_t)___GFP_NORETRY) /* See above */
    #define __GFP_MEMALLOC ((__force gfp_t)___GFP_MEMALLOC

    #define __GFP_COMP ((__force gfp_t)___GFP_COMP) /* Add compound page metadata */
    #define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) /* Return zeroed page on success */
    #define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC)   

    #define __GFP_HARDWALL   ((__force gfp_t)___GFP_HARDWALL)

    #define __GFP_THISNODE ((__force gfp_t)___GFP_THISNODE)/* No fallback, no policies */
    #define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) /* Page is reclaimable */
    #define __GFP_NOTRACK ((__force gfp_t)___GFP_NOTRACK)  /* Don't track with kmemcheck */

    #define __GFP_NO_KSWAPD ((__force gfp_t)___GFP_NO_KSWAPD)
    #define __GFP_OTHER_NODE ((__force gfp_t)___GFP_OTHER_NODE) /* On behalf of other node */
    #define __GFP_WRITE ((__force gfp_t)___GFP_WRITE) /* Allocator intends to dirty page */

    #define GFP_NOWAIT (GFP_ATOMIC & ~__GFP_HIGH)
   常用标志组合:

    #define GFP_ATOMIC (__GFP_HIGH)
    #define GFP_NOIO (__GFP_WAIT)
    #define GFP_NOFS (__GFP_WAIT | __GFP_IO)
    #define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS)
    #define GFP_TEMPORARY (__GFP_WAIT | __GFP_IO | __GFP_FS | \
    __GFP_RECLAIMABLE)
    #define GFP_USER (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
    #define GFP_HIGHUSER (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL | \
    __GFP_HIGHMEM)
    #define GFP_HIGHUSER_MOVABLE (__GFP_WAIT | __GFP_IO | __GFP_FS | \
     __GFP_HARDWALL | __GFP_HIGHMEM | \
     __GFP_MOVABLE)
    #define GFP_IOFS (__GFP_IO | __GFP_FS)
    #define GFP_TRANSHUGE (GFP_HIGHUSER_MOVABLE | __GFP_COMP | \
    __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN | \
    __GFP_NO_KSWAPD)

4,Slab分配器

    slab分配器用于在物理页框分配的基础,实现对更小内存空间或特定对象的管理。slab的基本思想是先利用页面分配器分配出单个或连续的物理页面,然后在将整块页面划分成多个相等的小内存单元,以满足小内存空间的需要。

    slab分配器把对象分组放进高速缓存,每个高速缓存都同种类型对象。高速缓存对kmem_cache_s表示,

高速缓存的内存区被划分为多个slab,每个slab由一个或多个连续的页框组成,这些页框包含已分配的对象和空闲对象。

    struct kmem_cache_s {
        struct kmem_list3 lists;
        unsigned int  objsize;
        unsigned int   flags; /* constant flags */
        unsigned int  num; /* # of objs per slab */
        unsigned int  free_limit; /* upper limit of objects in the lists */
        spinlock_t  spinlock;

        /* order of pgs per slab (2^n) */
        unsigned int  gfporder;

        const char  *name;
        struct list_head next;
    };

    struct kmem_list3 {
        struct list_head slabs_partial; /* partial list first, better asm code */
         struct list_head slabs_full;
         struct list_head slabs_free;
         unsigned long free_objects;
         int  free_touched;
         unsigned long next_reap;
         struct array_cache *shared;
       };

       struct slab {
          struct list_head list;
          unsigned long  colouroff;
          void   *s_mem;  /* including colour offset */
          unsigned int  inuse;  /* num of objs active in slab */
          kmem_bufctl_t  free;
       };

       

 


         

    size_cache:

    Linux内核称为general cache或default cache。是kmalloc函数实现的基础。
     struct cache_sizes {
        size_t   cs_size;
        kmem_cache_t *cs_cachep;
        kmem_cache_t *cs_dmacachep;
     };

    struct cache_sizes malloc_sizes[] = {
        #define CACHE(x) { .cs_size = (x) },
        #if (PAGE_SIZE == 4096)
        CACHE(32)
        #endif
        CACHE(64)
        #if L1_CACHE_BYTES < 64
        CACHE(96)
        #endif
        CACHE(128)
        #if L1_CACHE_BYTES < 128
        CACHE(192)
        #endif
        CACHE(256)
        CACHE(512)
        CACHE(1024)
        CACHE(2048)
        CACHE(4096)
        CACHE(8192)
        CACHE(16384)
        CACHE(32768)
        CACHE(65536)
        CACHE(131072)
       #ifndef CONFIG_MMU
        CACHE(262144)
        CACHE(524288)
        CACHE(1048576)
       #ifdef CONFIG_LARGE_ALLOCS
        CACHE(2097152)
        CACHE(4194304)
        CACHE(8388608)
        CACHE(16777216)
        CACHE(33554432)
       #endif /* CONFIG_LARGE_ALLOCS */
       #endif         { 0, }
        #undef CACHE
    };

 

       

    
5,分配API

    kmalloc函数是驱动程序中使用最多的内存分配函数,它分配出来的内存空间物理上是连续的,函数不负责内存空间的清空,它建立在slab分配器基础上,它实现主要是围绕size_cache展开。

    void * kmalloc(size_t size, int flags){

        struct cache_sizes *csizep = malloc_sizes;

        struct kmem_cache *cachep;

        while(size > csizep->size)

            csizep++

       cachep = csizep->cs_cachep;

        return kmem_cache_alloc(cachep, flags);

    }

    内核对象的分配:

    Linux内核源码中,大量利用kmem_cache_create来创建内核对象的缓存,相对于size_cache,内核模块开发者可以定满足自己特定要求的kmem_cache。

    struct kmem_cache* kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor) (void *))

    name是字符型指针,表示内核对象缓存的名称,size指定缓存的内核对象的大小。


    /**
     * kmem_cache_alloc - Allocate an object
     */
    void * kmem_cache_alloc (kmem_cache_t *cachep, int flags)

    /**
     * kmem_cache_destroy - delete a cache
     * @cachep: the cache to destroy
      */
    int kmem_cache_destroy (kmem_cache_t * cachep)

  • 大小: 125.2 KB
  • 大小: 251.7 KB
  • 大小: 275.6 KB
  • 大小: 136.9 KB
分享到:
评论

相关推荐

    linux内核内存管理

    Linux内核内存管理是操作系统中极为重要的一个部分,它涉及到操作系统如何高效、合理地使用物理内存资源以及虚拟内存资源。Linux内核内存管理机制包括页面类型与组织、页面回收逻辑、内存区域划分、页面分配策略、...

    Linux内核的内存管理探秘之三 物理内存的管理(二)页面周转与缓冲区.pdf

    "Linux内核的内存管理探秘之三 物理内存的管理(二)页面周转与缓冲区" Linux内核的内存管理探秘之三 物理内存的管理(二)页面周转与缓冲区是Linux内核的内存管理机制中一个重要的部分。物理内存的管理是指操作系统...

    一文剖析 Linux 内核的内存管理.docx

    Linux 内核的内存管理是一种复杂的机制,它的主要任务是对物理内存进行组织和分配,并对虚拟地址进行映射。下面将对 Linux 内核的内存管理机制进行详细的剖析。 一、物理内存的组织 Linux 中的物理内存被组织成三...

    Linux内核分析与应用课件第4章(四)物理内存分配与回收机制(二).pdf

    伙伴算法是Linux内核物理内存管理的关键,它将内存组织成一系列的页块链表,每个链表代表不同大小的页块,大小为2的幂次。当分配一个特定大小的页框时,算法会从对应的链表开始查找,若找不到合适的页块,就会尝试...

    Linux内核的内存管理探秘之四 虚拟内存的管理.pdf

    Linux 内核的内存管理探秘之四 虚拟内存的管理 Linux 操作系统中,内存管理是非常重要的一部分。虚拟内存技术是现代操作系统中的一个关键技术,它克服了旧有的内存管理的限制,允许系统运行比物理内存大的应用程序...

    linux内核内存管理图解

    Linux内核内存管理是操作系统设计中的关键部分,它负责有效地分配和管理系统的物理和虚拟内存。在Linux系统中,内存管理的复杂性在于它需要在多个进程之间共享有限的资源,同时确保系统的稳定性和高效性。以下是...

    Linux内核的内存管理探秘之二 物理内存的管理(一)分配与回收.pdf

    总的来说,Linux内核的物理内存管理机制是一个复杂而精细的系统,通过伙伴算法和Slab分配器的结合,有效地实现了内存的高效利用,降低了碎片问题,保证了系统的稳定运行。对于系统开发者和研究者来说,理解这一机制...

    Linux内核内存管理.zip

    Linux内核内存管理是操作系统设计的关键部分,它负责有效地分配、使用和回收系统中的物理内存。在Linux系统中,内存管理机制确保了高效且可靠的内存使用,为各个进程提供了一个一致且安全的运行环境。本资料主要探讨...

    Linux内核的内存管理探秘之一 进程线性区的管理.pdf

    Linux内核的内存管理机制可以分为两个部分:线性空间的管理和物理内存的管理。线性空间的管理是指进程的线性地址空间的管理,而物理内存的管理是指物理内存的分配和释放。 2.1 线性空间的概念 Linux内核从一开始...

    Linux011-内存管理

    1. **物理内存管理**: - Linux0.11内核通过页框管理器(Page Frame Manager)来分配和回收物理内存。页框是内存的基本单位,通常为4KB。 - 内存被分为多个连续的页框,这些页框在空闲链表中进行跟踪,当进程需要...

    Linux内核分析-内存篇

    Linux内核的内存管理是操作系统核心功能之一,负责高效地分配和回收物理内存资源。为了更好地理解这一复杂的过程,本文将深入探讨Linux内核如何管理和组织内存资源。 #### 内存布局 Linux内核将内存分为几个关键...

    Linux内存管理--Linux物理内存三级架构.pdf

    首先,内存节点(node)是Linux内核对物理内存的一种抽象表示。在一个计算机系统中,内存节点用于描述物理内存的分布情况。在一致存储结构(UMA)的计算机系统中,内存是均匀分布的,系统只有一个节点。然而,在非一致性...

    Linux内核内存管理技术分享

    Linux内核内存管理技术分享 Linux内核内存管理技术是指Linux操作系统中管理计算机内存资源的机制。该技术涉及到计算机体系结构、MMU、Cache、DMA、EPT、虚拟地址空间布局、伙伴系统、SLAB、用户空间地址布局、匿名...

    Linux内核设计与实现(第三版中文高清带目录)_linux_linux内核_

    虚拟内存系统允许程序使用超过实际物理内存的地址空间,通过交换技术在内存和磁盘之间动态调整。 4. **文件系统**:Linux支持多种文件系统,如EXT4、XFS、Btrfs等。文件系统管理磁盘上的数据结构,实现文件的创建、...

    Linux内核完全注释:基于0.11内核(V5.0)_0.11内核_linux_linux内核完全注释_Linux内核注释_

    3. 内存管理:了解Linux如何分配和回收内存,包括物理内存的组织、虚拟内存的映射、页面缓存等机制。这些知识对于优化程序性能和避免内存泄漏至关重要。 4. 文件系统:Linux内核支持多种文件系统,如EXT2、EXT3、...

    深入理解LINUX内核(中文第三版)第八章 内存管理1

    在Linux内核中,内存管理包括了物理内存的分配与释放、虚拟内存的映射、缓存和页面替换策略等多个方面。 首先,我们需要了解Linux内核是如何组织物理内存的。Linux采用了一种称为页式内存管理的机制,将物理内存...

    linux内核知识系列:内存管理

    Linux内核内存管理是操作系统设计的核心部分,它负责有效地分配、使用和回收系统中的物理及虚拟内存。在Linux系统中,内存管理确保了程序的高效运行,防止数据损坏,并优化了系统的整体性能。本篇文章将深入探讨...

    linux源代码分析之内存管理

    Zone是Linux内核内存管理中的一个重要概念,它将整个系统的物理内存分割成多个区域(zone),每个区域包含了一定量的物理页。这种分层的方式有助于提高内存分配的效率。每个Zone都有自己的管理结构,包括自旋锁、空闲...

    嵌入式课件\嵌入式Linux内核-内存管理

    本章节将详细介绍ARM-Linux内存管理的关键概念和技术,包括虚拟内存管理和物理内存管理。 #### 内存管理的本质特征 在ARM-Linux系统中,每个进程都需要占用一定的内存资源。但是,由于成本、体积和功耗等因素限制...

Global site tag (gtag.js) - Google Analytics