物理内存管理:请求PFN函数层次结构分析

http://lzz5235.github.io/2015/03/04/buddy-system.html

上篇讲到的伙伴系统是alloc_pages()族函数运行的基础,下面我来简单说明下alloc_pages()的结构。

alloc_pages()函数下存在五个子函数,他们最后都会调用到alloc_pages(),唯一的区别就是传入的参数不同。

###1.alloc_pages()

这个宏用来分配2的order次方个连续的页框,如果申请成功返回第一个所分配页框的描述符地址,这个地址是全局唯一的!申请失败的话返回NULL。


#define alloc_pages(gfp_mask, order) \
                alloc_pages_node(numa_node_id(), gfp_mask, order)

###2.alloc_page()

这个函数是alloc_pages的特殊情况,它只分配一个页框,也就是order等于0。


#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)

###3.__get_free_pages()

这个函数可以申请长为2的order次方大小的连续页框,但是它返回的是这段连续页框中第一个页所对应的线性地址(区别页框的描述符地址)。该函数内部仍然调用了alloc_pages(),并利用page_address()将页描述符地址转换为线性地址。


unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
        struct page *page;
        VM_BUG_ON((gfp_mask & __GFP_HIGHMEM) != 0);
        page = alloc_pages(gfp_mask, order);
        if (!page)
                return 0;
        return (unsigned long) page_address(page);
}

###4.__get_free_page()

该宏函数可以看作是__get_free_pages()的特殊情况,它用于申请一个单独的页框,然后返回这个单独页的线性地址。


#define __get_free_page(gfp_mask) \
        __get_free_pages((gfp_mask),0)

###5.get_zeroed_page()

该函数用来获取一个填满0的页框,其中__GFP_ZERO参数用来体现这一点,类似于memset()清零的效果。


unsigned long get_zeroed_page(gfp_t gfp_mask)
{
        return __get_free_pages(gfp_mask | __GFP_ZERO, 0);
}

###6.__get_dma_pages()

该宏函数获得的页框用于DMA操作。


#define (gfp_mask, order) \
                __get_free_pages((gfp_mask) | GFP_DMA,(order))

请求页框的标志通过查阅手册,我可以发现有非常多的mask。 如下代码


13 #define ___GFP_DMA              0x01u
14 #define ___GFP_HIGHMEM          0x02u
15 #define ___GFP_DMA32            0x04u
16 #define ___GFP_MOVABLE          0x08u
17 #define ___GFP_WAIT             0x10u
18 #define ___GFP_HIGH             0x20u
19 #define ___GFP_IO               0x40u
20 #define ___GFP_FS               0x80u
21 #define ___GFP_COLD             0x100u
22 #define ___GFP_NOWARN           0x200u
23 #define ___GFP_REPEAT           0x400u
24 #define ___GFP_NOFAIL           0x800u
25 #define ___GFP_NORETRY          0x1000u
26 #define ___GFP_MEMALLOC         0x2000u
27 #define ___GFP_COMP             0x4000u
28 #define ___GFP_ZERO             0x8000u
29 #define ___GFP_NOMEMALLOC       0x10000u
30 #define ___GFP_HARDWALL         0x20000u
31 #define ___GFP_THISNODE         0x40000u
32 #define ___GFP_RECLAIMABLE      0x80000u
33 #define ___GFP_NOTRACK          0x200000u
34 #define ___GFP_NO_KSWAPD        0x400000u
35 #define ___GFP_OTHER_NODE       0x800000u
36 #define ___GFP_WRITE            0x1000000u

使用最多的莫过于__GFP_DMA,GFP_HIGHMEM,_GFP_DMA32。 当我们在写内核模块的时候,我们会用到kmalloc()函数,里面的标志位最多的应该就是GFP_KERNEL、GFP_USER和GFP_ATOMIC。这三个参数经过层层解析被系统解析为


106 #define GFP_ATOMIC      (__GFP_HIGH)
109 #define GFP_KERNEL      (__GFP_WAIT | __GFP_IO | __GFP_FS)
112 #define GFP_USER        (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL)

系统中也会按照内存zone的区域分配,总的分配顺序是HIGHMEM、NORMAL、DMA ,如果API指定分配区域,系统就按照指定区域分配。

总的函数调用关系:


get_zeroed_page()      __get_free_page()   __get_dma_pages()
      |                        |                   |
      |                        |                   |
      |----------------------------------------------
 __get_free_pages()     alloc_page()
      |                        |
      |-------------------------
alloc_pages()
      |
alloc_pages_node()
      |
__alloc_pages()
      |
__alloc_pages_nodemask()

通过上面的结构表示,alloc_pages_node()是上述API的核心,而__alloc_pages_nodemask()为页框分配的心脏!

参考:

http://www.cnblogs.com/hanyan225/archive/2011/07/28/2119628.html

http://lxr.free-electrons.com/source/include/linux/gfp.h