物理内存的组织方式:每个页面都有一个结构,每一个都有节点( 三 )

  • compound 相关的变量用于复合页(Compound Page),就是将物理上连续的两个或多个页看成一个独立的大页 。
  •  
  • **第二种模式,仅需分配小块内存 。**有时候,我们不需要一下子分配这么多的内存,例如分配一个 task_struct 结构,只需要分配小块的内存,去存储这个进程描述结构的对象 。为了满足对这种小内存块的需要,Linux 系统采用了一种被称为slab allocator的技术,用于分配称为 slab 的一小块内存 。它的基本原理是从内存管理模块申请一整块页,然后划分成多个小块的存储池,用复杂的队列来维护这些小块的状态(状态包括:被分配了 / 被放回池子 / 应该被回收) 。
    也正是因为 slab allocator 对于队列的维护过于复杂,后来就有了一种不使用队列的分配器 slub allocator,后面我们会解析这个分配器 。但是你会发现,它里面还是用了很多 slab 的字眼,因为它保留了 slab 的用户接口,可以看成 slab allocator 的另一种实现 。
    还有一种小块内存的分配器称为slob,非常简单,主要使用在小型的嵌入式系统 。
    如果某一页是用于分割成一小块一小块的内存进行分配的使用模式,则会使用 union 中的以下变量:
    • s_mem 是已经分配了正在使用的 slab 的第一个对象;
    • freelist 是池子中的空闲对象;
    • rcu_head 是需要释放的列表 。
    struct page {unsigned long flags;union {struct address_space *mapping;void *s_mem;/* slab first object */atomic_t compound_mapcount; /* first tail page */};union {pgoff_t index;/* Our offset within mapping. */void *freelist;/* sl[aou]b first free object */};union {unsigned counters;struct {union {atomic_t _mapcount;unsigned int active;/* SLAB */struct {/* SLUB */unsigned inuse:16;unsigned objects:15;unsigned frozen:1;};int units;/* SLOB */};atomic_t _refcount;};};union {struct list_head lru; /* Pageout list*/struct dev_pagemap *pgmap;struct {/* slub per cpu partial pages */struct page *next; /* Next partial slab */int pages; /* Nr of partial slabs left */int pobjects; /* Approximate # of objects */};struct rcu_head rcu_head;struct {unsigned long compound_head; /* If bit zero is set */unsigned int compound_dtor;unsigned int compound_order;};};union {unsigned long private;struct kmem_cache *slab_cache; /* SL[AU]B: Pointer to slab */};......}页的分配好了,前面我们讲了物理内存的组织,从节点到区域到页到小块 。接下来,我们来看物理内存的分配 。
    对于要分配比较大的内存,例如到分配页级别的,可以使用伙伴系统(Buddy System) 。
    Linux 中的内存管理的“页”大小为 4KB 。把所有的空闲页分组为 11 个页块链表,每个块链表分别包含很多个大小的页块,有 1、2、4、8、16、32、64、128、256、512 和 1024 个连续页的页块 。最大可以申请 1024 个连续页,对应 4MB 大小的连续内存 。每个页块的第一个页的物理地址是该页块大小的整数倍 。
    物理内存的组织方式:每个页面都有一个结构,每一个都有节点

    文章插图
     
    第 i 个页块链表中,页块中页的数目为 2^i 。
    在 struct zone 里面有以下的定义:
    struct free_area free_area[MAX_ORDER];MAX_ORDER 就是指数 。
    #define MAX_ORDER 11当向内核请求分配 (2^(i-1),2^i] 数目的页块时,按照 2^i 页块请求处理 。如果对应的页块链表中没有空闲页块,那我们就在更大的页块链表中去找 。当分配的页块中有多余的页时,伙伴系统会根据多余的页块大小插入到对应的空闲页块链表中 。
    例如,要请求一个 128 个页的页块时,先检查 128 个页的页块链表是否有空闲块 。如果没有,则查 256 个页的页块链表;如果有空闲块的话,则将 256 个页的页块分成两份,一份使用,一份插入 128 个页的页块链表中 。如果还是没有,就查 512 个页的页块链表;如果有的话,就分裂为 128、128、256 三个页块,一个 128 的使用,剩余两个插入对应页块链表 。
    上面这个过程,我们可以在分配页的函数 alloc_pages 中看到 。
    static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order){ return alloc_pages_current(gfp_mask, order);}/** *alloc_pages_current - Allocate pages. * * @gfp: *%GFP_USERuser allocation, *%GFP_KERNEL kernel allocation, *%GFP_HIGHMEM highmem allocation, *%GFP_FSdon't call back into a file system. *%GFP_ATOMIC don't sleep. * @order: Power of two of allocation size in pages. 0 is a single page. * * Allocate a page from the kernel page pool.When not in * interrupt context and apply the current process NUMA policy. * Returns NULL when no page can be allocated. */struct page *alloc_pages_current(gfp_t gfp, unsigned order){ struct mempolicy *pol = &default_policy; struct page *page;...... page = __alloc_pages_nodemask(gfp, order,policy_node(gfp, pol, numa_node_id()),policy_nodemask(gfp, pol));...... return page;}


    推荐阅读