当前位置: 首页 > Linux kernrl, 内存管理 > 正文

内存管理器(十)kernel内存管理—-数据结构

内存管理器(十) kernel内存管理—-概况与数据结构

前言

说起内存管理,自然就是两个方面,物理内存管理,虚拟内存管理,我们先看看物理内存管理。
内核将物理页作为内存管理的基本单位。从虚拟内存的角度来看,页就是最小单位。32位机一般支持的是4KB的页,64位的机器一般就支持8KB的页,内核使用struct page 结构标示系统中的每一个物理页:
<linux/mm_types.h>

struct page {
    //First double word block
    unsigned long flags;        //Atomic flags, some possibly
    //页的状态   updated asynchronously 这里至少有32种状态 &lt;linux/page-flags.h&gt;
       union {
        struct address_space <em>mapping;  /</em> If low bit clear, points to
                                 * inode address_space, or NULL.
                         * If page mapped as anonymous
                         * memory, low bit is set, and
                         * it points to anon_vma object:
                         * see PAGE_MAPPING_ANON below.
                         <em>/
        void *s_mem;            /</em> slab first object <em>/
    };
/</em> Second double word <em>/<br />
        struct {
        union {
            pgoff_t index;      /</em> Our offset within mapping. <em>/
            void *freelist;     /</em> sl[aou]b first free object <em>/
            bool pfmemalloc;    /</em> If set by the page allocator,
                         * ALLOC_NO_WATERMARKS was set
                         * and the low watermark was not
                         * met implying that the system
                                 * is under some pressure. The
                         * caller should try ensure
                         * this page is only used to
                         * free other pages.
                         */
        };</p>

<p>union {
     #if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) &amp;&amp; \
    defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
            /* Used for cmpxchg_double in slub <em>/
            unsigned long counters;
                #else
            /</em>
             * Keep _count separate from slub cmpxchg_double data.
             * As the rest of the double word is protected by
             * slab_lock but _count is not.
             */
            unsigned counters;
                #endif
            struct {
                union {
                    /*
                     * Count of ptes mapped in
                     * mms, to show when page is
                     * mapped &amp; limit reverse map
                     * searches.
                     *
                     * Used also for tail pages
                     * refcounting instead of
                     * _count. Tail pages cannot
                     * be mapped and keeping the
                     * tail page _count zero at
                     * all times guarantees
                     * get_page_unless_zero() will
                     * never succeed on tail
                     * pages.
                     */
                    atomic_t _mapcount;
                    struct { /* SLUB */
                        unsigned inuse:16;
                        unsigned objects:15;
                        unsigned frozen:1;
                    };
                    int units;  /* SLOB */
                };
                atomic_t _count;        /* Usage count, see below. */
            };
            unsigned int active;    /* SLAB */
        };
    };
        atomic_t _count;      //页的引用计数,也就是这个页被引用了多少次
        //当计数值标变成-1 的时候就表示内核没有引用这一页,page_count()函数可以得到这个页的引用情况。
   /* Third double word block */
    union {
        struct list_head lru;   /* Pageout list, eg. active_list
                     * protected by zone-&gt;lru_lock !
                     * Can be used as a generic list
                     * by the page owner.
                     */
                //包含页的最近最少使用双向链表的指针
        struct {        /* slub per cpu partial pages */
            struct page *next;  /* Next partial slab */
       #ifdef CONFIG_64BIT
            int pages;  /* Nr of partial slabs left */
            int pobjects;   /* Approximate # of objects */
       #else
            short int pages;
            short int pobjects;
       #endif
        };
            struct slab *slab_page; /* slab fields */
        struct rcu_head rcu_head;   /* Used by SLAB
                         * when destroying via RCU
                         */
        /* First tail page of compound page */
        struct {
            compound_page_dtor *compound_dtor;
            unsigned long compound_order;
        };
        #if defined(CONFIG_TRANSPARENT_HUGEPAGE) &amp;&amp; USE_SPLIT_PMD_PTLOCKS
        pgtable_t pmd_huge_pte; /* protected by page-&gt;ptl */
        #endif
    };
        void *virtual;      //页的虚拟地址

这里只节选了这个结构体的一部分代码,不得不说换了博客以后真的有点不适应,这篇排版有点难看…

先说说几种体系架构吧(以下架构说明来自互联网)
SMP:
SMP(Symmetric Multi-Processor)

所谓对称多处理器结构,是指服务器中多个CPU对称工作,无主次或从属关系。各CPU共享相同的物理内存,每个 CPU访问内存中的任何地址所需时间是相同的,因此SMP也被称为一致存储器访问结构(UMA:Uniform Memory Access)。对SMP服务器进行扩展的方式包括增加内存、使用更快的CPU、增加CPU、扩充I/O(槽口数与总线数)以及添加更多的外部设备(通常是磁盘存储)。

SMP服务器的主要特征是共享,系统中所有资源(CPU、内存、I/O等)都是共享的。也正是由于这种特征,导致了SMP服务器的主要问题,那就是它的扩展能力非常有限。对于SMP服务器而言,每一个共享的环节都可能造成SMP服务器扩展时的瓶颈,而最受限制的则是内存。由于每个CPU必须通过相同的内存总线访问相同的内存资源,因此随着CPU数量的增加,内存访问冲突将迅速增加,最终会造成CPU资源的浪费,使 CPU性能的有效性大大降低。实验证明,SMP服务器CPU利用率最好的情况是2至4个CPU

内存毕竟是线性的模型,个人觉得寻址页是线性,确实是线性的,不过内核很聪明,它把内存根据地址情况画分成了不同的区。这里更多的是因为硬件的关系,导致内核并不能对所有的页一视同仁。每个内存节点的物理内存被划分为了不同的区。

NUMA:
NUMA服务器的基本特征是具有多个CPU模块,每个CPU模块由多个CPU(如4个)组成,并且具有独立的本地内存、I/O槽口等。由于其节点之间可以通过互联模块(如称为Crossbar Switch)进行连接和信息交互,因此每个CPU可以访问整个系统的内存(这是NUMA系统与MPP系统的重要差别)。显然,访问本地内存的速度将远远高于访问远地内存(系统内其它节点的内存)的速度,这也是非一致存储访问NUMA的由来。由于这个特点,为了更好地发挥系统性能,开发应用程序时需要尽量减少不同CPU模块之间的信息交互。利用NUMA技术,可以较好地解决原来SMP系统的扩展问题,在一个物理服务器内可以支持上百个CPU。比较典型的NUMA服务器的例子包括HP的Superdome、SUN15K、IBMp690等。

但NUMA技术同样有一定缺陷,由于访问远地内存的延时远远超过本地内存,因此当CPU数量增加时,系统性能无法线性增加。如HP公司发布Superdome服务器时,曾公布了它与HP其它UNIX服务器的相对性能值,结果发现,64路CPU的Superdome (NUMA结构)的相对性能值是20,而8路N4000(共享的SMP结构)的相对性能值是6.3。从这个结果可以看到,8倍数量的CPU换来的只是3倍性能的提升。

MPP:
和NUMA不同,MPP提供了另外一种进行系统扩展的方式,它由多个SMP服务器通过一定的节点互联网络进行连接,协同工作,完成相同的任务,从用户的角度来看是一个服务器系统。其基本特征是由多个SMP服务器(每个SMP服务器称节点)通过节点互联网络连接而成,每个节点只访问自己的本地资源(内存、存储等),是一种完全无共享(Share Nothing)结构,因而扩展能力最好,理论上其扩展无限制,目前的技术可实现512个节点互联,数千个CPU。目前业界对节点互联网络暂无标准,如 NCR的Bynet,IBM的SPSwitch,它们都采用了不同的内部实现机制。但节点互联网仅供MPP服务器内部使用,对用户而言是透明的。

在MPP系统中,每个SMP节点也可以运行自己的操作系统、数据库等。但和NUMA不同的是,它不存在异地内存访问的问题。换言之,每个节点内的CPU不能访问另一个节点的内存。节点之间的信息交互是通过节点互联网络实现的,这个过程一般称为数据重分配(Data Redistribution)。

但是MPP服务器需要一种复杂的机制来调度和平衡各个节点的负载和并行处理过程。目前一些基于MPP技术的服务器往往通过系统级软件(如数据库)来屏蔽这种复杂性。举例来说,NCR的Teradata就是基于MPP技术的一个关系数据库软件,基于此数据库来开发应用时,不管后台服务器由多少个节点组成,开发人员所面对的都是同一个数据库系统,而不需要考虑如何调度其中某几个节点的负载。

主要原因有下边的几个:
1.一些硬件只能用某些特定的内存地址来执行DMA(直接内存访问),所以虽然现在CPU有46位寻址,但并不代表计算机上所有的硬件都能寻址46位。
2.一些体系结构的内存寻址能力比虚拟地址范围大的多。
因为存在这些,Linux主要采用了几种分区:

enum zone_type {</p>

<h1>ifdef CONFIG_ZONE_DMA</h1>



		



<h1>endif</h1>

<h1>ifdef CONFIG_ZONE_DMA32</h1>



		



<h1>endif</h1>



		



<h1>ifdef CONFIG_HIGHMEM</h1>



		



<h1>endif</h1>



		



<p>};</p>

<p>

机智的linux 内核把系统的页划分为区,形成不同的内存池,这样就可以根据用途或者阶段来分配了,不过DMA区页时可以用来进行正常分配的,不过一般情况下会首先保证DMA够用。

现在我们再来看看 区的数据结构

struct zone {
    /* Read-mostly fields */</p>

<h1>ifdef CONFIG_NUMA //支持非一致性访问</h1>

<h1>endif</h1>

<h1>ifndef CONFIG_SPARSEMEM</h1>

<h1>endif /* CONFIG_SPARSEMEM */</h1> <h1>ifdef CONFIG_NUMA</h1>

<h1>endif /* CONFIG_NUMA */</h1>

<h1>ifdef CONFIG_MEMORY_ISOLATION</h1>

<h1>endif</h1> <h1>ifdef CONFIG_MEMORY_HOTPLUG</h1>

<h1>endif</h1>

<h1>if defined CONFIG_COMPACTION || defined CONFIG_CMA</h1>

<h1>endif</h1> <h1>ifdef CONFIG_COMPACTION</h1>

<h1>endif</h1> <h1>if defined CONFIG_COMPACTION || defined CONFIG_CMA</h1>

<h1>endif</h1>

<p>} ____cacheline_internodealigned_in_smp;</p> <p>

以上就是内核内存分配区的全部结构体,可以使用uname -a 来查看自己的电脑是什么体系结构的,我的电脑是SMP,根据上面所说的SMP 对称结构应当就是UMA 体系结构,所以我的系统一共就这样的三个区。关于NUMA的数据结构暂时不关心。

mlock

这里再说这样一个系统调用吧,mlock() 这个系统调用可以将,指定的内存页锁定在物理内存上,不让它进入交换区。对于对时间有严格要求的程序,这个操作很用。

本文固定链接: http://zmrlinux.com/2015/11/05/%e5%86%85%e5%ad%98%e7%ae%a1%e7%90%86%e5%99%a8%ef%bc%88%e5%8d%81%ef%bc%89kernel%e5%86%85%e5%ad%98%e7%ae%a1%e7%90%86-%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84/ | Kernel & Me

该日志由 root 于2015年11月05日发表在 Linux kernrl, 内存管理 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: 内存管理器(十)kernel内存管理—-数据结构 | Kernel & Me