Buddy_Zhang1
发表于 2016-03-15 10:42
回复 8# Tinnal
A. vmalloc 分配的地址可以直接使用,这是表层现象,我想研究的是当 vmalloc 分配的虚拟地址通过查页表找到 ZONE_HIGHMEM 的 page 之后
如何访问这个 page 对于的物理地址?
NORMAL 的虚拟地址找到对应的 page 之后通过 page_address() 可以找到 page 对应的物理地址.
Kmap 的虚拟地址找到对应的 page 之后,通过 page_address() 也可以找到 page 对应的物理地址.
但 vmalloc 分配的虚拟地址对应的 page 要通过什么方式找到对应的物理地址????
Tinnal
发表于 2016-03-15 11:40
本帖最后由 Tinnal 于 2016-03-15 11:40 编辑
明白你的根结所在了。
你的理解是:在访问对应的虚拟地址以后,CPU会通过页表找到page结构体,希望在page结构体里找到对应的物理地址,从而去访问真正的内存。
按你的理解推下去,因为目前的page结构体通过page_address拿不到物理地址, 因此就操作不了正真的内存, 因此你认为“分配的地址可以直接使用,这是表层现象”
从根本上你就错了。 page数组,在Linux里头称为页框管理结构。他只是用用来管理内框、登记页框信息的。CPU真正做虚拟内存到物理内存转化,只需要页表,与page结构体无关,更不用说page里的某一个成员。 这个页表在__vmalloc_area_node函数里早已经通过map_vm_area映射完毕了。 可以说,现在CPU已经可以独立的完成转换,与任务软件都无关,包括page结构。
page结构,只是为了让软件管理层知道,这段内存已经有人在用了。
Buddy_Zhang1
发表于 2016-03-15 12:09
回复 12# Tinnal
我的根本目的就是想知道内核如何操作物理内存的,是否只要提供 PTE 页表给内核,内核就可以获得 PTE 页表对应的 H/W PTE,这样访问物理地址就是硬件的事?????这样理解有错误吗?
为什么我会认为获得 page 之后通过 page_address() 获得对应的物理物理内存,然后对这部分内存进行操作,此时page_addree 返回的是虚拟地址,我也是通过 访问这些虚拟地址来访问物理内存.
如在 SLUB 中,从 buddy 获得一个 slab 页之后,就要在对应的 page 对应的物理内存上写数据:
static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node)
{
struct page *page;
void *start;
void *last;
void *p;
BUG_ON(flags & GFP_SLAB_BUG_MASK);
page = allocate_slab(s,
flags & (GFP_RECLAIM_MASK | GFP_CONSTRAINT_MASK), node);
if (!page)
goto out;
inc_slabs_node(s, page_to_nid(page), page->objects);
page->slab = s;
page->flags |= 1 << PG_slab;
start = page_address(page); // 这里获得 slab page 对应的物理内存
if (unlikely(s->flags & SLAB_POISON))
memset(start, POISON_INUSE, PAGE_SIZE << compound_order(page));
last = start;
for_each_object(p, s, start, page->objects) {
setup_object(s, page, last);
set_freepointer(s, last, p); // 这里直接对物理内存进行操作,此时通过虚拟地址访问.
last = p;
}
setup_object(s, page, last);
set_freepointer(s, last, NULL);
page->freelist = start;
page->inuse = 0;
out:
return page;
}
Buddy_Zhang1
发表于 2016-03-15 12:24
回复 12# Tinnal
更简单的说就是获得 page 之后,page 管理一段物理内存,我只要获得这段物理地址对应的虚拟地址进行操作就是访问这段物理内存.这样理解有错吗??????
Tinnal
发表于 2016-03-15 14:17
回复 14# Buddy_Zhang1
唉, 你还没有转过弯来。 没错没错。你申请完一直接用就行了别。哪虚拟地址用就行了。 不已经说了吗,CPU会给你去转换,不用访问你的page结构!
还有就是你的上一帖,这前也已经提到, 线性区可以用page_address取回这个page对应的虚拟地址。因为这个区域映射已经预先做好了,在预先做的时候,把页面对应的虚拟地址也一并写到page结构体的字段里头了。 面vmalloc是先拿高端内存,没有去这个操作。
再强调一下,一但配完MMU后,CPU就不用管PAGE了。
Buddy_Zhang1
发表于 2016-03-15 14:17
回复 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 , page , page , page....
3) 由于 struct page 是按顺序排列,所以可以高端物理内存和低端物理内存的 struct page 都在这个队列上,且低端内存位于前面,高端内存位于后面.
4) 通过这样的分布,我就可以获得 page 管理第一个物理区域, page 管理第二个物理区域...这是由于这样的基础,可以继续推断下面的:
a. PFN 为页帧号,也就是物理地址的序号,内核使用: PHYS_ADDR >> PAGE_SHIFT 获得.
b. 获得页帧号,在找对应的 struct page,也就是 0 号 PFN 对应 page ,内核使用宏 #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 对应的物理区间的原因,上面的分析有误的地方请指出.
Tinnal
发表于 2016-03-15 14:19
好好百度一个MMU的内容, LInux内存管理,找本书好好看看。再不明白,就是我表达能力问题。你新开一个贴,让其它人回答吧。
Buddy_Zhang1
发表于 2016-03-15 14:30
本帖最后由 Buddy_Zhang1 于 2016-03-15 14:30 编辑
回复 17# Tinnal
可能是我表达有问题,请听我解释一下.
我为什么要提出这些蛋疼的问题,是因为我最近有一个想法就是在用户空间分配256M 的数组,
然后基于这个数组上模拟一个类似 Linux 的系统,但这个系统只有内存管理部分.
这个系统的内存管理部分实现完全模拟 Linux 的内存管理.
问了上面的问题,因为我要在 256M 数组上模拟物理地址空间和虚拟地址空间,更确切的说我必须
去模拟 MMU 查表的过程.
现在到 vmalloc 分配器遇到地址访问问题,不能确定是不是由缺页引起的.所以一直提出些蛋疼的问题.
Tinnal
发表于 2016-03-16 00:57
又是蛋疼的实验,又是那个叫兽的题目。
要模拟,你就全模拟,把内存模型也模拟得了。你这叫半模拟吗,你知道你的程序无论怎么写都是访问虚拟地址的吗? 你在Linux系统里,根本就没办法直接访问物理地址。真实的MMU永无存在,你模拟什么呀。
还有你上一个贴,真不知道你想干麻。 第2步已经实现了虚拟地址到物理地址转换了, 第3部又希望通过page_address把page(可物理地址挂钩) 反过来得到虚拟地址。 我晕了,你究竟想怎么转??:dizzy:
你想真想用page_address, 不要再扯了,老老实实kmalloc一块出来用吧。我也不管你做什么蛋疼的实验了。
Buddy_Zhang1
发表于 2016-03-16 08:32
回复 19# Tinnal
首先,请您先学会尊师重教.
其次,这是我自己的个人行为,与他人无关.
第三,每个人都有自己的想法和想干的事,请不总用自己的世界观去评判一些事.请用客观的态度评判技术!!!!!!!!
最后,您作为前辈,请尊重别人的想法,世界需要不同的人干不同的事......