pgd_bad, pmd_bad, pud_bad, 转换内核地址是什么意思?

enf*_*net 2 kernel memory-management linux-kernel arm64 page-tables

有人可以解释一下内核页表中的宏吗?

#define pgd_bad(pgd)            (!(pgd_val(pgd) & 2))
#define pmd_bad(pmd)            (!(pmd_val(pmd) & 2))
#define pud_bad(pud)            (!(pud_val(pud) & 2))
Run Code Online (Sandbox Code Playgroud)

Lor*_*kes 5

正如最初定义的那样现在已经过时了(但很好) 了解 Linux 虚拟内存管理器_bad()宏确定指定的页表条目是否处于适合修改的状态。

然而,事实证明这些宏在不同架构中有些模糊,最好在 arm64 中描述为确定一个条目是否包含对包含下一级页表的表的页的引用 -有关详细信息,请参阅此提交

扩展@Notlikethat 的答案,宏确实引用了“表”位,这反过来(至少在 linux 内核使用中,可能有更详细的特定于体系结构的细节在起作用)确定条目的物理地址是否指向一个巨大的页面(64KiB - 请参阅arm64 内存布局文档)与否。

如果我们查看pte_huge()which 确定页面条目是否引用大页面,我们会看到:

#define pte_huge(pte)           (!(pte_val(pte) & PTE_TABLE_BIT))
Run Code Online (Sandbox Code Playgroud)

这表明如果该位被设置,页面大小是 4KiB,如果它被清除,它是 64KiB,因此是巨大的。

这得出的结论是,在 arm64 中,pXX_bad()如果使用大页面,宏将返回 true。

但是,如果启用了大表,您给出的定义实际上不会被使用:)

再次查看上面引用的内存布局文档,结果是大页表布局仅使用了 2 个级别。linux 处理少于 4 个页表级别的体系结构的方法是将不存在的级别“折叠”到现有表中,并让编译器删除所有不必要的代码。

如果我们查看arch/arm64/include/asm/pgtable.h上面的pgd_bad()定义(在撰写本文时在第 445 行),我们会看到:

#if CONFIG_PGTABLE_LEVELS > 3
Run Code Online (Sandbox Code Playgroud)

pud_bad()定义之上(在撰写本文时在第 392 行),我们看到:

#if CONFIG_PGTABLE_LEVELS > 2
Run Code Online (Sandbox Code Playgroud)

所以实际上因为CONFIG_PGTABLE_LEVELS == 2在大表的情况下,使用了不同的定义。

arch/arm64/include/asm/pgtable-types.h我们看到的(在在写作的时间线89):

#if CONFIG_PGTABLE_LEVELS == 2
#include <asm-generic/pgtable-nopmd.h>
#elif CONFIG_PGTABLE_LEVELS == 3
#include <asm-generic/pgtable-nopud.h>
#endif
Run Code Online (Sandbox Code Playgroud)

所以实际上使用了定义 from include/asm-generic/pgtable-nopmd.h,它本身会导入include/asm-generic/pgtable-nopud.h,给我们:

static inline int pgd_bad(pgd_t pgd)            { return 0; }
static inline int pud_bad(pud_t pud)            { return 0; }
Run Code Online (Sandbox Code Playgroud)

我们已经有了:

#define pmd_bad(pmd)            (!(pmd_val(pmd) & 2))
Run Code Online (Sandbox Code Playgroud)

这意味着任何pmd_bad()调用它的大页面 PMD 条目都将返回 true。但是,如果您查看我上面提到的提交,您会看到在提交之前,_bad()返回 true 将导致代码处理“部分映射”情况,其中 PMD 可能包含不同的元数据(我不想要潜水深在这里:)和单独的代码时,现在的通过处理pmd_sect(),我们不关心什么pmd_bad()说,如果该条目是一款地图。

看着arch/arm64/mm/mmu.cpmd_set_huge()(撰写本文时的第 828 行),似乎巨大的表 PMD 被设置为截面图,这意味着我们不需要关心pmd_bad()(我可能在这里弄错了,我没有深入研究或浏览过)使用调试器,但似乎是这样。)

因此,总体_bad()而言,现在的情况似乎有所不同 - 它只是表明一个错误导致了一些旨在处理非节映射/巨大页表条目的代码正在处理巨大的节映射页表条目,这显然需要要指出。

注意:由于我创建了一个帐户只是为了在这里回复,我没有足够的声誉来提供更多链接:) 一个有更多代表的好心人可能想在他们有意义的地方添加链接,例如“在第 XX 行”写作时间。)