内核中的物理内存分配函数kernel api解析(2)

__get_free_page() 是页面分配器提供给调用者的最底层的内存分配函数。它分配连续的物理内存。__get_free_page() 函数本身是基于 buddy 实现的。在使用 buddy 实现的物理内存管理中最小分配粒度是以页为单位的。关于以上论述,我们可以查看__get_free_page()的实现,可以看到__get_free_page()函数只是一个非常简单的封状,它的整个函数实现就是无条件的调用 __alloc_pages() 函数来分配物理内存,上面记录 kmalloc()实现时也提到过是在调用 __alloc_pages() 函数来分配物理页面的前提下进行的 slab 管理。那么这个函数是如何分配到物理页面又是在什么区域中进行分配的?回答这个问题只能看下相关的实现。可以看到在 __alloc_pages() 函数中,多次尝试调用get_page_from_freelist() 函数从 zonelist 中取得相关 zone,并从其中返回一个可用的 struct page 页面(这里的有些调用分支是因为标志不同)。至此,可以知道一个物理页面的分配是从 zonelist(一个 zone 的结构数组)中的 zone 返回的。那么 zonelist/zone 是如何与物理页面关联,又是如何初始化的呢?继续来看 free_area_init_nodes() 函数,此函数在系统初始化时由 zone_sizes_init() 函数间接调用的,zone_sizes_init()函数填充了三个区域:ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。并把他们作为参数调用 free_area_init_nodes(),在这个函数中会分配一个 pglist_data 结构,此结构中包含了 zonelist/zone结构和一个 struct page 的物理页结构,在函数最后用此结构作为参数调用了 free_area_init_node() 函数,在这个函数中首先使用 calculate_node_totalpages() 函数标记 pglist_data 相关区域,然后调用 alloc_node_mem_map() 函数初始化 pglist_data结构中的 struct page 物理页。最后使用 free_area_init_core()函数关联 pglist_data 与 zonelist。现在通以上分析已经明确了__get_free_page() 函数分配物理内存的流程。但这里又引出了几个新问题,那就是此函数分配的物理页面是如何映射的?映射到了什么位置?到这里不得不去看下与 VMM 相关的引导代码。

在看 VMM 相关的引导代码前,先来看一下 virt_to_phys() 与phys_to_virt 这两个函数。顾名思义,即是虚拟地址到物理地址和物理地址到虚拟地址的转换。函数实现十分简单,前者调用了__pa( address ) 转换虚拟地址到物理地址,后者调用 __va(addrress ) 将物理地址转换为虚拟地址。再看下 __pa __va 这两个宏到底做了什么。

#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)

#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/cb9a37dcc079fb23c56c1db196197b7d.html