The sizes of these bit fields are dictated by the structures they reference. For example, the byte offset is 12 bits because it denotes a byte within a page, and pages are 4,096 bytes (2 ^ 12 = 4,096). The other indexes are 10 bits because the structures they index have 1,024 entries (2 ^ 10 = 1,024). 这些位域的大小取决于它们所引用的结构。比如,“字节偏移”域是 12 位,因为该字段需要指示出页面内的一个字节,而页大小 一般为 4,096 字节(2 ^ 12 = 4,096)。(译注:按作者意思,12 位才可能够涵盖一个页中所有的字节) 其它的索引字段则是 10 位,因为它们索引的结构中,有 1,024 个条目(2 ^ 10 = 1,024) (译注:即,一个 PD[页目录] 中,有 1,024 个 PDE[页目录项],每个 PDE 索引一个 PT[页表]; 一个 PT 中,有 1,024 个 PTE[页表项],每个 PTE 索引一个范围 4,096 字节的页面)
The job of virtual address translation is to convert these virtual addresses into physical addresses— that is, addresses of locations in RAM. The format of a physical address on an x86 non-PAE system is shown in Figure 10-17. 虚拟地址翻译,这项工作就是把虚拟地址转换成物理地址——亦即,RAM(随机访问存储器)中位置的地址。在非 PAE 的 x86 系统上,物理地址的格式如图 10-17 所示。
As you can see, the format is very similar to that of a virtual address. Furthermore, the byte offset value from a virtual address will be the same in the resulting physical address. We can say, then, that address translation involves converting virtual page numbers to physical page numbers (also referred to as page frame numbers, or PFNs). The byte offset does not participate in, and does not change as a result of, address translation. It is simply copied from the virtual address to the physical address, 如你所见,它的格式与虚拟地址非常相似。此外,转换产生的物理地址中的“字节偏移”值,将与原虚拟地址中的相同。 如此一来,我们可以说,地址翻译涉及把虚拟页号转换成物理页号(亦称为“页框号”,或 PFN)。事实上,“字节偏移”并不参与转换过程,也不因地址转换的结果而改变——只是简单地把它从虚拟地址复制到物理地址中。
Figure 10-18 shows the relationship of these three values and how they are used to perform address translation. 图 10-18 描绘了这三个值(字段)之间的关系,以及它们如何被用来执行地址翻译。
The following basic steps are involved in translating a virtual address: 1. The memory management unit (MMU) uses a privileged CPU register, CR3, to obtain the physical address of the page directory. 2. The page directory index portion of the virtual address is used as an index into the page directory. This locates the page directory entry (PDE) that contains the location of the page table needed to map the virtual address. The PDE in turn contains the physical page number, also called the page frame number, or PFN, of the desired page table, provided the page table is resident—page tables can be paged out or not yet created, and in those cases, the page table is first made resident before proceeding. If a flag in the PDE indicates that it describes a large page, then it simply contains the PFN of the target large page, and the rest of the virtual address is treated as the byte offset within the large page. 3. The page table index is used as an index into the page table to locate the PTE that describes the virtual page in question. 4. If the PTE’s valid bit is clear, this triggers a page fault (memory management fault). The operating system’s memory management fault handler (pager) locates the page and tries to make it valid; after doing so, this sequence continues at step 5. (See the section “Page Fault Handling.”) If the page cannot or should not be made valid (for example, because of a protection fault), the fault handler generates an access violation or a bug check. 5. When the PTE describes a valid page (whether immediately or after page fault resolution), the desired physical address is constructed from the PFN field of the PTE, followed by the byte offset field from the original virtual address. Now that you have the overall picture, let’s look at the detailed structure of page directories, page tables, and PTEs.
On non-PAE x86 systems, each process has a single page directory, a page the memory manager creates to map the location of all page tables for that process. The physical address of the process page directory is stored in the kernel process (KPROCESS) block, but it is also mapped virtually at address 0xC0300000 on x86 non-PAE systems. (For more detailed information about the KPROCESS and other process data structures, refer to Chapter 5, “Processes, Threads, and Jobs” in Part 1.)
页目录
在非 PAE 的 x86 系统上,每个进程都有单一的页目录,这是内存管理器创建来映射该进程所有页表位置的页面。 进程页目录的物理地址存储在“内核进程块”(KPROCESS)中,但在 x86 非 PAE 系统上,它也虚拟地映射到地址 0xC0300000 处。 (更多有关 KPROCESS 和其它进程数据结构的信息,请参考本书上册第五章“Processes, Threads, and Jobs”)
The CPU obtains the location of the page directory from a privileged CPU register called CR3. It contains the page frame number of the page directory. (Since the page directory is itself always page-aligned, the low-order 12 bits of its address are always zero, so there is no need for CR3 to supply these.) Each time a context switch occurs to a thread that is in a different process than that of the currently executing thread, the context switch routine in the kernel loads this register from a field in the KPROCESS block of the new process. Context switches between threads in the same process don’t result in reloading the physical address of the page directory because all threads within the same process share the same process address space and thus use the same page directory and page tables.
The page directory is composed of page directory entries (PDEs), each of which is 4 bytes long. The PDEs in the page directory describe the state and location of all the possible page tables for the process. As described later in the chapter, page tables are created on demand, so the page directory for most processes points only to a small set of page tables. (If a page table does not yet exist, the VAD tree is consulted to determine whether an access should materialize it.) The format of a PDE isn’t repeated here because it’s mostly the same as a hardware PTE, which is described shortly. 页目录由页目录项(PDE)组成,每个 PDE 大小为四字节。页目录中的 PDE 描述所有可能用于该进程页表的状态与位置。 正如本章稍后所述,页表是按需创建的,因此多数进程的页目录仅指向很小的一组页表。 (假设一张页表尚不存在,就会查阅 VAD 树,以确定是否应该实现对它的访问——译者提问:即创建页表?) 此处不会重复介绍 PDE 的格式,因为它与硬件 PTE 大致相同,稍后会对其进行讨论。
To describe the full 4-GB virtual address space, 1,024 page tables are required. The process page directory that maps these page tables contains 1,024 PDEs. Therefore, the page directory index needs to be 10 bits wide (2 ^ 10 = 1,024). 为了能够描述整个 4GB 的虚拟地址空间,需要 1,024 张页表。所以,映射这些页表的进程页目录就包含 1,024 个 PDE(译注: 每个 PDE 描述一张页表)。因此,页目录索引的宽度必须是 10 位(2 ^ 10 = 1,024)。
EXPERIMENT: Examining the Page Directory and PDEs You can see the physical address of the currently running process’s page directory by examining the DirBase field in the !process kernel debugger output:
The PTE part of the kernel debugger output is defined in the section “Page Tables and Page Table Entries.” We will describe this output further in the section on x86 PAE translation.
Because Windows provides a private address space for each process, each process has its own
page directory and page tables to map that process’s private address space. However, the page tables that describe system space are shared among all processes (and session space is shared only among processes in a session). To avoid having multiple page tables describing the same virtual memory, when a process is created, the page directory entries that describe system space are initialized to point to the existing system page tables. If the process is part of a session, session space page tables are also shared by pointing the session space page directory entries to the existing session page tables. 由于 Windows 为每个进程提供了各自私有的地址空间,每个进程就有自己的页目录和页表来映射自身的私有地址空间。
Each page directory entry points to a page table. A page table is a simple array of PTEs. The virtual address’s page table index field (as shown in Figure 10-18) indicates which PTE within the page table corresponds to and describes the data page in question. The page table index is 10 bits wide, allowing you to reference up to 1,024 4-byte PTEs.
Of course, because x86 provides a 4-GB virtual address space, more than one page table is needed to map the entire address space. To calculate the number of page tables required to map the entire 4-GB virtual address space, divide 4 GB by the virtual memory mapped by a single page table. Recall that each page table on an x86 system maps 4 MB of data pages. Thus, 1,024 page tables (4 GB / 4 MB) are required to map the full 4-GB address space. This corresponds with the 1,024 entries in the page directory.
You can use the !pte command in the kernel debugger to examine PTEs. (See the experiment “Translating Addresses.”) We’ll discuss valid PTEs here and invalid PTEs in a later section. Valid PTEs have two main fields: the page frame number (PFN) of the physical page containing the data or of the physical address of a page in memory, and some flags that describe the state and protection of the page, as shown in Figure 10-19. 你可以使用内核调试器的“!pte”命令查看 PTE。(参考实验:“地址翻译”) 此处我们将讨论有效的 PTE,在后续部分讨论无效的 PTE。有效 PTE 的两个主要字段,即物理页(包含数据)的页框号(PFN), 或 RAM 中页面物理地址的页框号,另一个字段则是描述该页面状态和保护信息的一些标志位,如图 10-19 所示。
As you’ll see later, the bits labeled “Software field” and “Reserved” in Figure 10-19 are ignored by the MMU, whether or not the PTE is valid. These bits are stored and interpreted by the memory manager. Table 10-11 briefly describes the hardware-defined bits in a valid PTE. 正如您稍候将要看到的,CPU 的内存管理单元(MMU)会忽略掉图 10-19 中,被标记为“Software field”和 “Reserved”的位, 无论这个 PTE 是否有效。图 10-19 中的这些位由内存管理器存储并解释。表 10-11 将一个有效 PTE 中,那些硬件定义的位作了简短 介绍。
On x86 systems, a hardware PTE contains two bits that can be changed by the MMU, the Dirty bit and the Accessed bit. The MMU sets the Accessed bit whenever the page is read or written (provided it is not already set). The MMU sets the Dirty bit whenever a write operation occurs to the page. The operating system is responsible for clearing these bits at the appropriate times; they are never cleared by the MMU. 在 x86 系统上,硬件 PTE 包含两个可由 MMU 更改的比特位,即“脏位”和“访问位”。每当该页面被读取或写入时,MMU 就会设置 访问位(只要它未被设置)。 每当该页面发生一次写操作时,MMU 就会设置脏位。操作系统负责在适当的时间清除这些位;MMU 从不清除它们。
The x86 MMU uses a Write bit to provide page protection. When this bit is clear, the page is readonly; when it is set, the page is read/write. If a thread attempts to write to a page with the Write bit clear, a memory management exception occurs, and the memory manager’s access fault handler (described later in the chapter) must determine whether the thread can be allowed to write to the page (for example, if the page was really marked copy-on-write) or whether an access violation should be generated. x86 MMU 借助“Write bit”来提供页面保护机制。当此位清 0 时,对应的页面是只读的;当此位置 1 时,对应的页面可读写。 如果一个线程试图向“Write bit”清 0 的页面写入,就会引发一次内存管理异常,而内存管理器的“访问错误处理程序”( 本章稍后会讲到)必须确定:该线程是否被允许写入这个页面(比如,目标页面确实标记了“写时复制”),或者是否应该产生 一个“非法访问”异常。
Hardware vs. Software Write Bits in Page Table Entries
The additional Write bit implemented in software (as mentioned in Table 10-11) is used to force updating of the Dirty bit to be synchronized with updates to Windows memory management data. In a simple implementation, the memory manager would set the hardware Write bit (bit 1) for any writable page, and a write to any such page will cause the MMU to set the Dirty bit in the page table entry. Later, the Dirty bit will tell the memory manager that the contents of that physical page must be written to backing store before the physical page can be used for something else.
In practice, on multiprocessor systems, this can lead to race conditions that are expensive to resolve. The MMUs of the various processors can, at any time, set the Dirty bit of any PTE that has its hardware Write bit set. The memory manager must, at various times, update the process working set list to reflect the state of the Dirty bit in a PTE. The memory manager uses a pushlock to synchronize access to the working set list. But on a multiprocessor system, even while one processor is holding the lock, the Dirty bit might be changed by MMUs of other CPUs. This raises the possibility of missing an update to a Dirty bit. 在实践中,对于多处理系统,这可能导致竞争条件,解决它的代价很昂贵。 各处理器的 MMU 们,都可以在任何时候设置任何 PTE 的“脏位”(它们的硬写位已置 1)。相应地,内存管理器就必须在不同时间 更新进程的工作集列表,以反映该 PTE 中“脏位”的状态。 (译注:有效 PTE 引用的进程页面驻留在物理内存中,它们合称工作集。所以要把对某个 PTE 的修改,更新到它引用的进程页面—— 这就是更新该进程的工作集) 内存管理器使用“推锁”来同步对工作集列表的访问。然而在多处理器系统上,即便当一个处理器持有该锁,其它 CPU 的 MMU 们, 也可以修改同一个 PTE 的“脏位”。这就可能会错过对“脏位”的相应更新。
To avoid this, the Windows memory manager initializes both read-only and writable pages with the hardware Write bit (bit 1) of their PTEs set to 0 and records the true writable state of the page in the software Write bit (bit 11). On the first write access to such a page, the processor will raise a memory management exception because the hardware Write bit is clear, just as it would be for a true read-only page. In this case, though, the memory manager learns that the page actually is writable (via the software Write bit), acquires the working set pushlock, sets the Dirty bit and the hardware Write bit in the PTE, updates the working set list to note that the page has been changed, releases the working set pushlock, and dismisses the exception. The hardware write operation then proceeds as usual, but the setting of the Dirty bit is made to happen with the working set list pushlock held. 为了避免这种情况,Windows 内存管理器就把描述只读和可写页的那些 PTE 中的硬写位(位 1),都初始化成 0,然后在软写位( 位 11)中,记录该页面的实际可写状态。 首次向此类页面执行写访问时,处理器将会引发一次内存管理异常,因为硬写位是清除的,仿佛一个真正的只读页那样。 然而在这种情况下,内存管理器通过软写位获悉该页面实际上是可写的,它就会取得工作集推锁,设置 PTE 中的“脏位”和硬写位, 更新工作集列表,以通知该页面的变动,释放工作集推锁,并且驳回异常。 接着一如既往地进行硬件写操作,而在持有工作集锁列表推锁后,就会发生对“脏位”的设置。
On subsequent writes to the page, no exceptions occur because the hardware Write bit is set. The MMU will redundantly set the Dirty bit, but this is benign because the “written-to” state of the page is already recorded in the working set list. Forcing the first write to a page to go through this exception handling may seem to be excessive overhead. However, it happens only once per writable page as long as the page remains valid. Furthermore, the first access to almost any page already goes through memory management exception handling because pages are usually initialized in the invalid state (PTE bit 0 is clear). If the first access to a page is also the first write access to the page, the Dirty bit handling just described will occur within the handling of the first-access page fault, so the additional overhead is small. Finally, on both uniprocessor and multiprocessor systems, this implementation allows flushing of the translation look-aside buffer (described later) without holding a lock for each page being flushed.
Once the memory manager has determined the physical page number, it must locate the requested data within that page. This is the purpose of the byte offset field. The byte offset from the original virtual address is simply copied to the corresponding field in the physical address. On x86 systems, the byte offset is 12 bits wide, allowing you to reference up to 4,096 bytes of data (the size of a page). Another way to interpret this is that the byte offset from the virtual address is concatenated to the physical page number retrieved from the PTE. This completes the translation of a virtual address to a physical address.
As you’ve learned so far, each hardware address translation requires two lookups: one to find the right entry in the page directory (which provides the location of the page table) and one to find the right entry in the page table. Because doing two additional memory lookups for every reference to a virtual address would triple the required bandwidth to memory, resulting in poor performance, all CPUs cache address translations so that repeated accesses to the same addresses don’t have to be repeatedly translated. This cache is an array of associative memory called the translation look-aside buffer, or TLB. Associative memory is a vector whose cells can be read simultaneously and compared to a target value. In the case of the TLB, the vector contains the virtual-to-physical page mappings of the most recently used pages, as shown in Figure 10-20, and the type of page protection, size, attributes, and so on applied to each page. Each entry in the TLB is like a cache entry whose tag holds portions of the virtual address and whose data portion holds a physical page number, protection field, valid bit, and usually a dirty bit indicating the condition of the page to which the cached PTE corresponds. If a PTE’s global bit is set (as is done by Windows for system space pages that are visible to all processes), the TLB entry isn’t invalidated on process context switches.
Virtual addresses that are used frequently are likely to have entries in the TLB, which provides extremely fast virtual-to-physical address translation and, therefore, fast memory access. If a virtual address isn’t in the TLB, it might still be in memory, but multiple memory accesses are needed to find it, which makes the access time slightly slower. If a virtual page has been paged out of memory or if the memory manager changes the PTE, the memory manager is required to explicitly invalidate the TLB entry. If a process accesses it again, a page fault occurs, and the memory manager brings the page back into memory (if needed) and re-creates its PTE entry (which then results in an entry for it in the TLB). 频繁用到的虚拟地址很可能在 TLB 中有对应的条目,这就提供了极快的虚拟-物理地址转换过程,故而快速的内存访问(译注:只需 一次物理内存访问即可取出指令或代码,这就是为啥无论在所需时间和带宽上,都只有“循规蹈矩”地址翻译的三分之一。) 如果某个虚拟地址在 TLB 中没有对应的项,就可能仍在内存中,而需要多次主存储器访问才能找到,使得访问时间稍慢一些(与 页面被换出内存相比)。 如果虚拟页已经被换出主存储器,或者内存管理器变更了 PTE,它就需要显式地让相应的 TLB 条目失效。(译注:这很好理解: PTE 描述的物理页变了,原 TLB 中记录的当然是错误的转换结果) 当一个进程再次访问已经被换出的虚拟页时,就会发生一次页错误,然后内存管理器把该页面换回内存(如有必要),并重建它的 PTE 项(而后导致在 TLB 中缓存相应的条目)。
The Intel x86 Pentium Pro processor introduced a memory-mapping mode called Physical Address Extension (PAE). With the proper chipset, the PAE mode allows 32-bit operating systems access to up to 64 GB of physical memory on current Intel x86 processors (up from 4 GB without PAE) and up to 1,024 GB of physical memory when running on x64 processors in legacy mode (although Windows currently limits this to 64 GB due to the size of the PFN database required to describe so much memory). When the processor is running in PAE mode, the memory management unit (MMU) divides virtual addresses mapped by normal pages into four fields, as shown in Figure 10-21. The MMU still implements page directories and page tables, but under PAE a third level, the page directory pointer table, exists above them.
One way in which 32-bit applications can take advantage of such large memory configurations is described in the earlier section “Address Windowing Extensions.” However, even if applications are not using such functions, the memory manager will use all available physical memory for multiple processes’ working sets, file cache, and trimmed private data through the use of the system cache, standby, and modified lists (described in the section “Page Frame Number Database”).
PAE mode is selected at boot time and cannot be changed without rebooting. As explained in Chapter 2 in Part 1, there is a special version of the 32-bit Windows kernel with support for PAE called Ntkrnlpa.exe. Thirty-two-bit systems that have hardware support for nonexecutable memory (described earlier, in the section “No Execute Page Protection”) are booted by default using this PAE kernel, because PAE mode is required to implement the no-execute feature. To force the loading of the PAE-enabled kernel, you can set the pae BCD option to ForceEnable.
Note that the PAE kernel is installed on the disk on all 32-bit Windows systems, even systems with small memory and without hardware no-execute support. This is to allow testing of PAE-related code, even on small memory systems, and to avoid the need for reinstalling Windows should more RAM be added later. Another BCD option relevant to PAE is nolowmem, which discards memory below 4 GB (assuming you have at least 5 GB of physical memory) and relocates device drivers above this range. This guarantees that drivers will be presented with physical addresses greater than 32 bits, which makes any possible driver sign extension bugs easier to find.
To understand PAE, it is useful to understand the derivation of the sizes of the various structures and bit fields. Recall that the goal of PAE is to allow addressing of more than 4 GB of RAM. The 4-GB limit for RAM addresses without PAE comes from the 12-bit byte offset and the 20-bit page frame number fields of physical addresses: 12 + 20 = 32 bits of physical address, and 232 bytes = 4 GB. (Note that this is due to a limit of the physical address format and the number of bits allocated for the PFN within a page table entry. The fact that virtual addresses are 32 bits wide on x86, with or without PAE, does not limit the physical address space.) Under PAE, the PFN is expanded to 24 bits. Combined with the 12-bit byte offset, this allows addressing of 224 + 12 bytes, or 64 GB, of memory.
To provide the 24-bit PFN, PAE expands the PFN fields of page table and page directory entries from 20 to 24 bits. To allow room for this expansion, the page table and page directory entries are 8 bytes wide instead of 4. (This would seem to expand the PFN field of the PTE and PDE by 32 bits rather than just 4, but in x86 processors, PFNs are limited to 24 bits. This does leave a large number of bits in the PDE unused—or, rather, available for future expansion.) Since both page tables and page directories have to fit in one page, these tables can then have only 512 entries instead of 1,024. So the corresponding index fields of the virtual address are accordingly reduced from 10 to 9 bits.
This then leaves the two high-order bits of the virtual address unaccounted for. So PAE expands the number of page directories from one to four and adds a third-level address translation table, called the page directory pointer table, or PDPT. This table contains only four entries, 8 bytes each, which provide the PFNs of the four page directories. The two high-order bits of the virtual address are used to index into the PDPT and are called the page directory pointer index.
As before, CR3 provides the location of the top-level table, but that is now the PDPT rather than the page directory. The PDPT must be aligned on a 32-byte boundary and must furthermore reside in the first 4 GB of RAM (because CR3 on x86 is only a 32-bit register, even with PAE enabled). Note that PAE mode can address more memory than the standard translation mode not directly because of the extra level of translation, but because the physical address format has been expanded. The extra level of translation is required to allow processing of all 32 bits of a virtual address.
To clarify how address translation works, this experiment shows a real example of translating a virtual address on an x86 PAE system, using the available tools in the kernel debugger to examine the PDPT, page directories, page tables, and PTEs. (It is common for Windows on today’s x86 processors, even with less than 4 GB of RAM, to run in PAE mode because PAE mode is required to enable no-execute memory access protection.) In this example, we’ll work with a process that has virtual address 0x30004, currently mapped to a valid physical address. In later examples, you’ll see how to follow address translation for invalid addresses with the kernel debugger. First let’s convert 0x30004 to binary and break it into the three fields that are used to translate an address. In binary, 0x30004 is 11.0000.0000.0000.0100. Breaking it into the component fields yields the following:
实验:地址转译
本实验会演示在一个 x86 PAE 系统上转译虚拟地址的真实案例,以便把地址转译的原理说清楚讲明白,使用内核调试器中可用的工具(命令),来审查 PDPT,页目录,页表,以及页表项。 (如前所述——作者比我还罗嗦——在当今启用了 PAE 模式的 x86 处理器上运行 Windows 是很常见的,甚至少于 4 GB 物理内存的系统也运行在 PAE 模式下,因为需要 PAE 模式才能够启用不可执行内存的访问保护。) 在这个例子中,我们将要剖析的进程虚拟地址为 0x30004,当前被映射到一个有效的物理地址。而在稍后的例子中,你将看到如何通过内核调试器来为无效地址跟踪它的转换过程。 1。首先,我们把 0x30004 转换为二进制表示,再将其分解成用于翻译地址的三个字段。0x30004 的二进制表示为“11.0000.0000.0000.0100”。分解后的组成字段如下图所示:
To start the translation process, the CPU needs the physical address of the process’s page directory pointer table, found in the CR3 register while a thread in that process is running. You can display this address by looking at the DirBase field in the output of the !process command, as shown here:
The DirBase field shows that the page directory pointer table is at physical address 0xced25440. As shown in the preceding illustration, the page directory pointer table index field in our example virtual address is 0. Therefore, the PDPT entry that contains the physical address of the relevant page directory is the first entry in the PDPT, at physical address 0xced25440. As under x86 non-PAE systems, the kernel debugger !pte command displays the PDE and PTE that describe a virtual address, as shown here:
Here we have used the debugger extension command !dq. This is similar to the dq command (display as quadwords—“quadwords” being a name for a 64-bit field; this came from the day when “words” were often 16 bits), but it lets us examine memory by physical rather than virtual address. Since we know that the PDPT is only four entries long, we added the L 4 length argument to keep the output uncluttered.
As illustrated previously, the PDPT index (the two most significant bits) from our example virtual address equal 0, so the PDPT entry we want is the first displayed quadword. PDPT entries have a format similar to PD entries and PT entries, so we can see by inspection that this one contains a PFN of 0x2e8ff, for a physical address of 2e8ff000. That’s the physical address of the page directory.
The !pte output shows the PDE address as a virtual address, not physical. On x86 systems with PAE, the first process page directory starts at virtual address 0xC0600000. The page directory index field of our example virtual address is 0, so we’re looking at the first PDE in the page directory. Therefore, in this case, the PDE address is the same as the page directory address.
As with non-PAE, the page directory entry provides the PFN of the needed page table; in this example, the PFN is 0x2ebf3. So the page table starts at physical address 0x2ebf3000. To this the MMU will add the page table index field (0x30) from the virtual address, multiplied by 8 (the size of a PTE in bytes; this would be 4 on a non-PAE system). The resulting physical address of the PTE is then 0x2ebf3180.
The debugger shows that this PTE is at virtual address 0xC0000180. Notice that the byte offset portion (0x180) is the same as that from the physical address, as is always the case in address translation. Because the memory manager maps page tables starting at 0xC0000000, adding 0x180 to 0xC0000000 yields the virtual address shown in the kernel debugger output: 0xC0000180. The debugger shows that the PFN field of the PTE is 0x5af4d.
Finally, we can consider the byte offset from the original address. As described previously, the MMU will concatenate the byte offset to the PFN from the PTE, giving a physical address of 0x5af4d004. This is the physical address that corresponds to the original virtual address of 0x30004—at the moment.
The flags bits from the PTE are interpreted to the right of the PFN number. For example, the PTE that describes the page being referenced has flags of --A--UR-V. Here, A stands for accessed (the page has been read), U for user-mode accessible (as opposed to kernel-mode accessible only), R for read-only page (rather than writable), and V for valid (the PTE represents a valid page in physical memory).
To confirm our calculation of the physical address, we can look at the memory in question via both its virtual and its physical addresses. First, using the debugger’s dd command (display dwords) on the virtual address, we see the following: