- 论坛徽章:
- 9
|
回复 12# Tinnal
" 从根本上你就错了。 page数组,在Linux里头称为页框管理结构。他只是用用来管理内框、登记页框信息的。CPU真正做虚拟内存到物理内存转化,只需要页表,与page结构体无关,更不用说page里的某一个成员。 这个页表在__vmalloc_area_node函数里早已经通过map_vm_area映射完毕了。 可以说,现在CPU已经可以独立的完成转换,与任务软件都无关,包括page结构。"
对于上面的说法,我还是有自己的看法,先上源码:
在内核启动期间,调用 free_area_init_node() 将全部物理页数量(包括 ZONE_HIGHMEM) 存储在 NODE_DATA(0)->node_spanned_pages 里面.
然后内核调用 alloc_node_mem_map() ,里面关键代码实现如下:
static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
{
...
if (!pgdat->node_mem_map) {
unsigned long size, start, end;
struct page *map;
start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);
end = pgdat->node_start_pfn + pgdat->node_spanned_pages;
end = ALIGN(end, MAX_ORDER_NR_PAGES);
size = (end - start) * sizeof(struct page);
map = alloc_remap(pgdat->node_id, size);
if (!map)
map = alloc_bootmem_node(pgdat, size);
pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
}
if (pgdat == NODE_DATA(0)) {
mem_map = NODE_DATA(0)->node_mem_map;
...
}
1. 从上面的代码可知,内核启动过程中,先统计物理内存的数量,单位为页.将统计的值存在 NODE_DATA(0)->node_spanned_pages 里面,这里面包括 ZONE_HIGHMEM 的页,
页包括一些 Hole.
2. 由于该期间还是启动期间,只有 bootmem 分配器可以使用,内核就从 bootmem 中分配了 (NODE_DATA(0)->node_spanned_pages) 个 sizeof(struct page) 的内存用来存储
所以的 struct page 结构.
3. 分配好内存之后,如果 mem_map 指向这段内存.到这里可以理解为:
1) 物理内存使用一个 struct page 来表示大小为 PAGE_SIZE 的物理区域.
2) 所有的 struct page 结构都是线性排列,也就是 page[0] , page[1] , page[2] , page[3]....
3) 由于 struct page 是按顺序排列,所以可以高端物理内存和低端物理内存的 struct page 都在这个队列上,且低端内存位于前面,高端内存位于后面.
4) 通过这样的分布,我就可以获得 page[0] 管理第一个物理区域, page[1] 管理第二个物理区域... 这是由于这样的基础,可以继续推断下面的:
a. PFN 为页帧号,也就是物理地址的序号,内核使用: PHYS_ADDR >> PAGE_SHIFT 获得.
b. 获得页帧号,在找对应的 struct page,也就是 0 号 PFN 对应 page[0] ,内核使用宏 #define pfn_to_page(pfn) (mem_map + ((pfn) - PHYS_PFN_OFFSET))
c. 通过上面的推到,我就可以得出这样的结论,只要我知道 page ,就可以获得对应的物理内存.其实现如下:
phys_addr = page_to_pfn(page) << PAGE_SHIFT;
以上推断对于 ZONE_NORMAL 没有问题,关键是对于 ZONE_HIGHMEM 的 page.
1. 如我使用 vmalloc() 分配之后获得内核的虚拟地址 vaddr
vaddr = vmalloc(PAGE_SIZE);
2. 通过查表找到 vaddr 对应的 page,其实现如下:
struct page *page;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
pgd = pgd_offset(&init_mm,vaddr);
pmd = pmd_offset(pgd,vaddr);
pte = pte_offset_kernel(pmd,vaddr);
page = pte_page(pte);
此处 page 就是 vaddr 对应的物理页,然后获得其页帧号 pfn = page_to_pfn(page);
在获得其物理地址 pfn << PAGE_SHIFT;
然后这段物理空间就是 vaddr 对应的物理区间,然后对其进行读写操作就可以了.
3. 这里在获得 page_address() 的原因是,page_address() 处理 ZONE_NORMAL 的 page 和 KAMP 方式的 ZONE_HIGHMEM page.
4. 如果我想通过 vaddr 快速获得物理地址的话就可以通过 TLB 了.
上面就是我要去操作 page 对应的物理区间的原因,上面的分析有误的地方请指出.
|
|