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

March 13th, 2015 by JasonLe's Tech Leave a reply »

http://www.lizhaozhong.info/archives/1254

上篇讲到的伙伴系统是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