type
status
slug
summary
tags
category
icon
password
new update day
Property
Oct 22, 2023 01:31 PM
created days
Last edited time
Oct 22, 2023 01:31 PM
put_page 是 Linux 0.11 内核中的一个函数,它用于将物理内存页面映射到进程的虚拟地址空间中。它接受两个参数:pageaddresspage 是要映射的物理页面的地址,而 address 是要映射到的虚拟地址。
下面是 put_page 函数在 Linux 0.11 中的实现(位于 linux/mm/memory.c 文件中):
该函数首先根据传入的虚拟地址 address 计算出对应的页目录项地址,并将 page_table 指针设置为指向该页目录项。然后检查该页目录项是否有效(即对应的页表是否存在)。如果有效,则将 page_table 指针设置为指向该页目录项对应的页表;否则,使用 get_free_page 函数申请一个新的物理页面作为页表,并将该页表的地址写入页目录项中。
接下来,函数根据传入的虚拟地址 address 计算出对应的页表项索引,并将传入的物理页面地址 page 写入该页表项中。最后,返回传入的物理页面地址 page

一个示例

下面是一个简单的示例,用于解释这行代码的工作原理。
假设我们有一个虚拟地址 address,其值为 0x12345678。我们希望将一个物理页面映射到该虚拟地址所在的页面。
首先,我们需要计算出该虚拟地址对应的页目录项地址。在 Linux 0.11 中,每个进程都有一个页目录,它是一个包含 1024 个页目录项的数组。每个页目录项占用 4 个字节,用于存储对应页表的地址。
由于每个页目录项占用 4 个字节,因此页目录的总大小为 1024 * 4 = 4096 字节,即 4KB。Linux 0.11 将每个进程的页目录放在其虚拟地址空间的最后 4KB 处,因此页目录的起始地址为 0xfffff000
现在,我们需要根据虚拟地址 address 计算出对应的页目录项索引。由于每个页表包含 1024 个页表项,每个页表项对应一个页面(大小为 4KB),因此每个页目录项可以映射 1024 * 4KB = 4MB 的虚拟地址空间。因此,我们可以通过将虚拟地址右移 22 位(即除以 2^22 = 4MB)来计算出对应的页目录项索引。
在本例中,虚拟地址 address 的值为 0x12345678,右移 22 位后得到 0x48。这意味着我们需要访问第 0x48 个页目录项。
由于每个页目录项占用 4 个字节,因此第 0x48 个页目录项的地址为 0xfffff000 + 0x48 * 4 = 0xfffff120。这就是我们要访问的页目录项的地址。
现在让我们回到代码中:
这行代码首先将虚拟地址 address 右移 20 位(即除以 2^20 = 1MB),然后与 0xffc 进行按位与运算。由于 0xffc = 0b111111111100,因此这相当于保留了右移后结果的最高 10 位,并将其他位清零。
在本例中,虚拟地址 address 的值为 0x12345678,右移 20 位后得到 0x123。将其与 0xffc 进行按位与运算后得到 0x120。这正是我们要访问的页目录项相对于页目录起始地址的偏移量。
最后,代码将计算出的偏移量强制转换为指针类型,并赋值给指针变量 page_table。由于没有指定基地址,因此这里实际上是将偏移量作为绝对地址来使用。由于 Linux 0.11 将每个进程的页目录放在其虚拟地址空间的最后 4KB 处(即起始地址为 0xfffff000),因此这相当于将指针变量 page_table 设置为指向第 0x48 个页目录项。
 
xv6 _entry 堆栈设置汇编代码解析Linux 0.11 中的内存地址空间概念