arm 内存管理页表问题咨询
本文是原「蜗窝讨论区」的历史存档(2017-09-13),来自版块「Linux kernel技术问答」,共 7 帖。讨论区已停止服务,此处仅供查阅。
想了解下Linux页表相关内容,看的云里雾里
被mmu页表和Linux软件页表绕晕了
综合各种原因,最终arm-linux假装第一级目录只有2048个条目,但其实每个条目是2个ulong大小即8字节,所以最终设置MMU的还是4096个条目,只是每访问1个pgd条目将可以访问到2个pte条目,linux为了实现其内存管理功能又在后面加上2个对应的假pte表,这个假pte表专门给linux内核代码自己用的,不会影响arm硬件
如上描述mm指向的pgd实际是Linux软件页目录和页表?不影响mmu,那mmu通过什么地址转换呢?
还有我在测试的时候看initmm对应的pgd在create_mapping建立了1M的段表,这个是内核用的还是mmu用的?
我看其他的一些mm->pgd指向的内存地址还是有二级页表的,晕了
求大神指导
又看了下create_mapping的实现,看上去如果是1m的块内存,直接就把内存的首地址放到pgd的条目中了
不足1m的先申请页表空间,按4k循环建立页表填充页表项,最后将页表的首地址填充到pgd条目中。
1m的内存pgd[0]和pgd[1]是分别映射了1m的空间,而后面的pte页表pgd[0]和pgd[1]似乎赋值一样属性不一样。
这两种mmu怎么区分,如果一个地址过来怎么知道他是断映射还是二级映射?
方便把代码贴出来吗?要不不知道你说的那一段代码。
linuxer 写道: 方便把代码贴出来吗?要不不知道你说的那一段代码。
alloc_init_section
static void __init alloc_init_section(pud_t pud, unsigned long addr, unsigned long end, phys_addr_t phys, const struct mem_type type, bool force_pages) { pmd_t *pmd = pmd_offset(pud, addr);
/*
* Try a section mapping - end, addr and phys must all be aligned
* to a section boundary. Note that PMDs refer to the individual
* L1 entries, whereas PGDs refer to a group of L1 entries making
* up one logical pointer to an L2 table.
*/
if (((addr | end | phys) & ~SECTION_MASK) == 0 && !force_pages) {
pmd_t *p = pmd;
ifndef CONFIG_ARM_LPAE
if (addr & SECTION_SIZE)
pmd++;
endif
do {
*pmd = __pmd(phys | type->prot_sect);
phys += SECTION_SIZE;
} while (pmd++, addr += SECTION_SIZE, addr != end);
flush_pmd_entry(p);
} else {
/*
* No need to loop; pte’s aren’t interested in the
* individual L1 entries.
*/
alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);
}
}
不太明白pgd两个long型的意义,作为一级映射和二级映射时候,这两个long型值意义不一样,一个普通的地址怎么知道他是一级映射了还是二级映射的
这个地方的确比较绕,之所以如此主要是HW和SW之间不匹配。在linux内核中,PGD/PMD/PTE分别对应地址中的12/8/12的比特,但是HW的页表和linux的设定不是非常一致,是11/9/12。
在pgtable-2level.h中,Russell king 描述了 ARM的页表为迎合Linux kernel, 而做的特殊设计。虚拟地址【31..20】等bit作为PGD表中的索引,所以PGD有4096项,虚拟地址的[19..12]等8个bit为二级页表的索引,所以共有256项。[11..0]共12个bit为一个page内的偏移。page大小设定为4K。这是ARM MMU硬件可以支持的方式,但是Linux kernel 希望一个PTE 表能够占满一个page(4K),并且至少要有dirty bits(实际上ARM Linux kernel支持dirty young等bits),为此ARM Linux Kernel 选择了如下方式:
逻辑上PGD索引只有11个bit[31-21],每一项的尺寸是8个字节。而PTE有9个bit[20-12],共512项。一个page仍然是4K。为此PTE一级引入了Linux pt 和 hw pt的概念。Linux PT 主要定义一些Linux 用的权限标志,比如YONG FILE DIRTY等(这些宏以L_PTE_打头,定义在pgtable-2level.h中 ),hw pt还是硬件的 页表标志,也就是前面一段提到的内容,定义在pgtable-2level-hwdef.h中。逻辑上PGD含有2048项,每一项有8个字节,其实就是两个指向二级页表的描述符。这样二级页表按如下格式布局:
两个硬件页表连续存放,他们的前面存放的是Linux 页表,Linux 页表中存放了Linux kernel需要的硬件信息。两个硬件页表,两个Linux 页表 ,每一个页表都是256项,他们占满了一个page 4K (一共1024个表项,每一项4个byte).
linuxer 写道: 这个地方的确比较绕,之所以如此主要是HW和SW之间不匹配。在linux内核中,PGD/PMD/PTE分别对应地址中的12/8/12的比特,但是HW的页表和linux的设定不是非常一致,是11/9/12 ......
非常感谢了,知道了原因,我再从这个方向去理解下,再看看这块。
还有个问题也想一起问下 arm_memblock_init和bootmem_init,这两个网上找的资料经常说memblock代替了bootmem。 create_mapping好像也用到了memblock申请内存,为什么接着就到了bootmem_init,然后mem_map都用bootmem申请内存。
那个和内核配置相关,可以使用memblock,也可以使用传统的bootmem。
