我在阅读head.s(Linux内核文件之一)时遇到了一个问题

我在阅读head.s(Linux内核文件之一)时遇到了一个问题,linux,linux-kernel,Linux,Linux Kernel,head.s中的代码片段如下所示: movl $swapper_pg_dir-0xc0000000,%eax movl %eax,%cr3 /* set the page table pointer.. */ movl %cr0,%eax orl $0x80000000,%eax movl %eax,%cr0 /* ..and set paging (PG) bit */ 在内核启用分页机制之前(当然,PE标志现在已经启用),它会将临时页面目录表的地址加载到%cr3中 问题是: 我认为内核应该

head.s
中的代码片段如下所示:

movl $swapper_pg_dir-0xc0000000,%eax
movl %eax,%cr3 /* set the page table pointer.. */
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /* ..and set paging (PG) bit */
在内核启用分页机制之前(当然,PE标志现在已经启用),它会将临时页面目录表的地址加载到
%cr3

问题是:

我认为内核应该直接将
$swapper\u pg\u dir
值放入
%eax
中,而不是
$swapper\u pg\u dir-0xc0000000
。我知道我错了,但为什么

内核将内存视为基于0xC0000000。任何内存分配(指针或全局)都位于0xC0000000到0xFFFFFF之间。 然而,对于硬件控制器,如MMU或任何协处理器,内存窗口通常基于0x00000000


因此,当将指向表或描述符的指针加载到硬件引擎时,它必须基于0x00000000

OP会从
swapper\u pg\u dir
询问为什么Linux减去
0xc000000
。这个计算的原因是
%cr3
需要物理地址,而
$swapper\u pg\u dir
是一个虚拟地址。在内核页面初始化的初始阶段,8MB RAM有两个映射(
0到0x007fffff
)。对于虚拟地址
0x00000000-0x007fffff
,第一个映射是
1对1
,而另一个映射是
0xc000000-0xcoo7FFF
。为了获得
swapper\u pg\u dir
的物理地址,我们必须从
swapper\u pg\u dir
的地址中减去
0xc000000

你好,@adashi,对不起,我是Linux内核的新手,所以我无法理解你的回答。你能详细说明一下吗?我认为$swapper\u pg\u dir-0xc0000000不是正整数,我想知道为什么我们不将$swapper\u pg\u dir加载到%eax中,$swapper\u pg\u dir是物理地址,不是吗?不。在构建Linux内核时,链接器将“内核空间”(内核使用范围内的虚拟地址)中的地址分配给所有内核符号。因此,
$swapper\u pg\u dir
将计算为虚拟地址,该地址仅在设置页表后可用
$swapper\u pg\u dir-0xc0000000
是相应的物理地址。正如Alex所指出的,内核链接到地址0xc0000000。用户空间中的每个进程都有自己的虚拟内存映射,因此当指向同一地址(例如1MB)时,多个进程会看到不同的内存页。但是,在执行内核代码时(例如,在调用系统调用时),所有进程都在相同的虚拟(但线性)地址空间中执行内核代码-从0xc0000000开始。0xc0000000是历史值,取决于32位地址空间、PC系统中的内存漏洞和系统内存大小等变量,在当时是可以接受的。