打印内核的页表项

具有4级页表的虚拟内存映射:

0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm ffff800000000000 - ffff80ffffffffff (=40 bits) guard hole ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all phys. memory ffffc80000000000 - ffffc8ffffffffff (=40 bits) hole ffffc90000000000 - ffffe8ffffffffff (=45 bits) vmalloc/ioremap space ffffe90000000000 - ffffe9ffffffffff (=40 bits) hole ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB) ffffffff80000000 - ffffffffa0000000 (=512 MB) kernel text mapping, from phys 0 ffffffffa0000000 - fffffffffff00000 (=1536 MB) module mapping space 

我知道内核试图直接映射物理地址到从直接映射区域的PAGE_OFFSET开始的虚拟地址

 ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all phys. memory 

但我不知道内核中的代码在哪里保持页表来pipe理这个直接映射区域,以及如何打印出在这个直接映射区域中的4级页表的所有页表项。 你知道如何打印他们吗?

火影忍者:

我也有兴趣打印Linux的页表,我偶然发现了内核源码中的漂亮的实用程序

 arch/x86/mm/dump_pagetables.c 

如果你用CONFIG_X86_PTDUMP设置你的内核 ,你可以这样做

 cat /sys/kernel/debug/kernel_page_tables 

它将遍历内核的页表结构并将其打印出来。 然而输出有两个问题:

  1. 它只列出了虚拟地址,而不是它们相关的物理地址。 这让我很失望,因为我最感兴趣的是虚拟映射到非RAM系统地址(如PCI空间等) – 在其他作品中,我希望在/ proc / iomem中以/ sys /核心/调试/ kernel_page_tables。
  2. 它不提供打印每个进程页表的方法。 据我所知,每个linux进程和init_level_4_pgt(内核的原始(启动后)页表)共享他们的虚拟地址空间的一大部分:从病房的PAGE_OFFSET准确。 我想这样做是为了让用户进程(通过系统调用)的上下文中运行的内核代码可以访问内核内存。 无论如何,我认为看到所有这些行动都是非常好的。

这里是我修补的3.13.11内核的链接 ,修复了这两个问题(修复的分支是page_table_print)。

要使用,只需做

 echo <pid_of_interest> > /proc/sys/debug/pgt_dump_process_id 

其中pid_of_interest如果您希望监视其页表的进程的PID。 注意:做

 echo -1 > /proc/sys/debug/pgt_dump_process_id 

这将转储内核的页表。 以下是内核页表的输出示例:

 cat /sys/kernel/debug/kernel_page_tables | more CR3= 0x196b70000, va_CR3 = 0xffff880196b70000 Page tables for kernelPGT localtion in memory: phs = 0x1c0e000, virt = 0xffffffff81c0e000 ---[ User Space ]--- 0x0000000000000000-0xffff800000000000, phy: 0x0000000000000000-0x0000800001c0e000 16777088T pgd ---[ coreel Space ]--- 0xffff800000000000-0xffff880000000000, phy: 0x0000800001c0e000-0x0000000001fe1000 8T pgd ---[ Low coreel Mapping ]--- 0xffff880000000000-0xffff880000096000, phy: 0x0000000001fe1000-0x0000000002077000 600K RW GLB NX pte 0xffff880000096000-0xffff880000097000, phy: 0x0000000002077000-0x0000000002078000 4K ro GLB NX pte 0xffff880000097000-0xffff880000098000, phy: 0x0000000002078000-0x0000000002079000 4K ro GLB x pte 0xffff880000098000-0xffff880000200000, phy: 0x0000000002079000-0x00000000021e0000 1440K RW GLB NX pte 0xffff880000200000-0xffff880001000000, phy: 0x00000000021e0000-0x0000000002fe0000 14M RW PSE GLB NX pmd 0xffff880001000000-0xffff880001600000, phy: 0x0000000002fe0000-0x00000001b14a1000 6M ro PSE GLB NX pmd 0xffff880001600000-0xffff880001734000, phy: 0x00000001b14a1000-0x00000001b15d5000 1232K ro GLB NX pte 0xffff880001734000-0xffff880001800000, phy: 0x00000001b15d5000-0x00000000037e0000 816K RW GLB NX pte 0xffff880001800000-0xffff880001a00000, phy: 0x00000000037e0000-0x00000001b14a2000 2M ro PSE GLB NX pmd 

这是做完之后的输出

 echo 1 > /proc/sys/debug/pgt_dump_process_id cat /sys/kernel/debug/kernel_page_tables | more CR3= 0x17312f000, va_CR3 = 0xffff88017312f000 Page tables for process id = 1 PGT localtion in memory: phs = 0x3623f000, virt = 0xffff88003623f000 ---[ User Space ]--- 0x0000000000000000-0x00007f8000000000, phy: 0x0000000000000000-0x0000000036277000 130560G pgd 0x00007f8000000000-0x00007ff200000000, phy: 0x0000000036277000-0x000000003627a000 456G pud 0x00007ff200000000-0x00007ff207800000, phy: 0x000000003627a000-0x0000000036097000 120M pmd 0x00007ff207800000-0x00007ff2079b5000, phy: 0x0000000036097000-0x000000003624c000 1748K pte 0x00007ff2079b5000-0x00007ff2079b8000, phy: 0x000000003624c000-0x000000003624f000 12K USR ro x pte 0x00007ff2079b8000-0x00007ff207bbf000, phy: 0x000000003624f000-0x00000000363c3000 2076K pte 0x00007ff207bbf000-0x00007ff207bc1000, phy: 0x00000000363c3000-0x00000000363c5000 8K USR ro NX pte 0x00007ff207bc1000-0x00007ff207bc4000, phy: 0x00000000363c5000-0x00000000363c8000 12K USR ro x pte 0x00007ff207bc4000-0x00007ff207bc6000, phy: 0x00000000363c8000-0x00000000363ca000 8K pte 0x00007ff207bc6000-0x00007ff207bc7000, phy: 0x00000000363ca000-0x00000000363cb000 4K USR ro x pte 0x00007ff207bc7000-0x00007ff207dcb000, phy: 0x00000000363cb000-0x00000000363d0000 2064K pte 0x00007ff207dcb000-0x00007ff207dcd000, phy: 0x00000000363d0000-0x00000000363d2000 8K USR ro NX pte 0x00007ff207dcd000-0x00007ff207dd2000, phy: 0x00000000363d2000-0x00000000363d7000 20K USR ro x pte 0x00007ff207dd2000-0x00007ff207fe3000, phy: 0x00000000363d7000-0x00000000363fe000 2116K pte 0x00007ff207fe3000-0x00007ff207fe5000, phy: 0x00000000363fe000-0x0000000036400000 8K USR ro NX pte 0x00007ff207fe5000-0x00007ff207fe7000, phy: 0x0000000036400000-0x0000000036402000 8K pte 0x00007ff207fe7000-0x00007ff207fec000, phy: 0x0000000036402000-0x0000000036407000 20K USR ro x pte 0x00007ff207fec000-0x00007ff207fed000, phy: 0x0000000036407000-0x0000000036408000 4K pte 0x00007ff207fed000-0x00007ff207fef000, phy: 0x0000000036408000-0x000000003640a000 8K USR ro x pte 0x00007ff207fef000-0x00007ff2081ef000, phy: 0x000000003640a000-0x0000000036214000 2M pte 0x00007ff2081ef000-0x00007ff2081f1000, phy: 0x0000000036214000-0x0000000036216000 8K USR ro NX pte 

我觉得这很整齐,希望别人也一样。 🙂

每个进程都有自己的页面全局目录(PGD),其指针存储在CR3寄存器中,该进程在内核运行时加载。 内核也保留自己的过程交换器的PGD(如果我没记错的话),它存储在init_mm.pgd结构成员中。 你应该能够在这个PGD中找到你正在寻找的页面。

不幸的是,除非你在虚拟机中运行,否则我知道没有简单的方法到达CR3。 你可以使用GDB来获取init_mm结构体,如下所示:

  • 获取与当前内核匹配的调试符号的内核。 匹配必须精确,所以最好使用相应的分发包。 请参阅如何获取Ubuntu调试Ubuntu内核方向的内核 。

  • 假设你正在运行一个运行/ proc / kcore(例如Ubuntu)的发行版,你可以执行gdb <debugguing kernel image> /proc/kcore

  • 现在做p init_mm.pgd获取内核的PGD地址。

  • 你现在可以转储使用dump mem <file name> <pgd start> <pgd start + 0x1000> (这就是如果PGD适合4K页面)

为了获得与你感兴趣的内存范围相对应的所有较低级别的表,你必须从PGD开始,手动或者在GDB中使用python脚本。