温暖的电波  

在NUMA架构中存在多个内存节点,每个内存节点在Linux内核用pg_data_t类型(实际是struct pglist_data)来表示表示;

各个内存节点又各自有各自的内存区(即struct zone),这些内存区由pg_data_t的成员struct zone node_zones[MAX_NR_ZONES]来存放;

而在分配内存时不仅可以从自己的内存节点分配内存,还可从其他内存节点分配内存,选择哪个节点分配内存也是提前有策略设计好了的,这个是通过Linux内核一些数据结构的巧妙设计来决策,这个就是由pg_data_t的成员struct zonelist node_zonelists[MAX_ZONELISTS]来实现的。

typedef struct pglist_data {
    /*
    * node_zones contains just the zones for THIS node. Not all of the
    * zones may be populated, but it is the full list. It is referenced by
    * this node's node_zonelists as well as other node's node_zonelists.
    */
    struct zone node_zones[MAX_NR_ZONES];             //存放该内存节点的内存区zones

    /*
    * node_zonelists contains references to all zones in all nodes.
    * Generally the first zones will be references to this node's
    * node_zones.
    */
    struct zonelist node_zonelists[MAX_ZONELISTS]; //zonelist装着系统中所有内存节点的zones
    ......
} pg_data_t;

struct zonelist {
    struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];    //一个zonelist最多可以有(MAX_ZONES_PER_ZONELIST + 1)个struct zoneref
};


//一个zoneref对应一个zone
/*
 * This struct contains information about a zone in a zonelist. It is stored
 * here to avoid dereferences into large structures and lookups of tables
 */
struct zoneref {
    struct zone *zone;      /* Pointer to actual zone */
    int zone_idx;           /* zone_idx(zoneref->zone) */
};

上面就是一个节点与其他节点zones发生关系的一些相关数据结构。还是来一张图清楚一点:

       

 

 

 图1  struct pglist_data与struct zone、struct zonelist关系示意图

 

pg_data_t->node_zones[MAX_NR_ZONES]:该内存节点的MAX_NR_ZONE个内存区(struct zone)。

                                                                          具体多少视系统支持哪些类型的struct zone,例如ZONE_DMA, ZONE_DMA32, ZONE_NORMAL, ZONE_HIGH.....。有的架构可能没有ZONE_DMA,有的可能没有ZONE_DMA32,有的可能没有ZONE_HIGH。

pg_data_t->node_zonelists[MAX_ZONELISTS]:定义了该内存节点分配内存时选择从struct zone分配内存的先后顺序。

                      在支持NUMA的内核中最多两个选择:ZONELIST_FALLBACK和ZONELIST_NOFALLBACK,如下所示

enum {
    ZONELIST_FALLBACK,      /* zonelist with fallback */
#ifdef CONFIG_NUMA
    /*
     * The NUMA zonelists are doubled because we need zonelists that
     * restrict the allocations to a single node for __GFP_THISNODE.
     */
    ZONELIST_NOFALLBACK,    /* zonelist without fallback (__GFP_THISNODE) */
#endif
    MAX_ZONELISTS
};

从注释可以了解到:ZONELIST_NOFALLBACK是在NUMA架构中,如果分配内存时使用__GFP_THISNODE了标志时则尝试从pg_data_t->node_zonelists[ZONELIST_NOFALLBACK]中去分配内存;否则尝试从pg_data_t->node_zonelists[ZONELIST_FALLBACK]中去分配内存。

在系统初始化阶段,Linux内核会遍历系统中的各个内存节点,并根据其他邻居节点到该节点"距离"的"远""近"关系安插邻居节点的struct zone到pg_data_t->node_zonelists[ZONELIST_FALLBACK]的数组中,即zonelist->_zonerefs[MAX_ZONES_PER_ZONELIST+1]这个数组中:

struct zonelist {
    struct zoneref _zonerefs[MAX_ZONES_PER_ZONELIST + 1];    //一个zonelist最多可以有(MAX_ZONES_PER_ZONELIST + 1)个struct zoneref
};

"距离"越近的邻居节点所属的struct zone,在_zonerefs[]数组中的位置越靠前;同一个邻居节点中zones按照从高到低的顺序排列(即ZONE_HIGH、ZONE_NORMAL...ZONE_DMA)。

这样在分配内存时,优先从最近的邻居内存节点的最高index的内存zone分配内存,这样就达到了前文所说的分配顺序策略的遴选情况。

 

posted on 2021-04-07 22:36  温暖的电波  阅读(1172)  评论(0)    收藏  举报