硬件提供了一个内存管理单元。 它是一个电路,可以拦截和更改任何内存访问。 每当处理器访问RAM时,例如 要读取下一条要执行的指令或作为一条指令触发的数据访问,它会在某个地址(大致来说是32位值)上执行此操作。 一个32位的字可以有40亿个不同的值,因此地址空间为4 GB:这是可以具有唯一地址的字节数。
因此,处理器将请求发送到其内存子系统,如“在地址x处获取字节并将其还给我”。 该请求通过MMU,后者决定如何处理该请求。 MMU实际上将4 GB的空间分成了几页; 页面大小取决于您使用的硬件,但是典型大小是4和8 kB。 MMU使用的表告诉它如何处理每个页面的访问:授予访问权限的地址是重写的(页面条目显示:“是,包含地址x的页面存在,它位于地址y的物理RAM中”) 或拒绝),这时将调用内核以进一步处理事务。 内核可以决定终止令人讨厌的进程,或者做一些工作并更改MMU表,以便可以再次尝试访问,这次成功了。
这是虚拟内存的基础:从角度来看,该进程具有一些RAM,但是内核已将其移至硬盘的“交换空间”中。 MMU表中相应的表标记为“不存在”。当进程访问他的数据时,MMU调用内核,其获取来自于交换数据,把它背在物理RAM改变MMU表,以点在该领域的一些自由空间,。 然后内核跳回到触发整个事件的指令处的过程代码。 流程代码看不到整个业务,只是内存访问花费了相当长的时间。
MMU还处理访问权限,这可防止进程读取或写入属于其他进程或内核的数据。每个进程都有自己的一套MMU表,内核管理这些表。 因此,每个进程都有自己的地址空间,就好像它是单独在具有4 GB RAM的机器上一样-除非该进程最好不要访问未从内核正确分配的内存,因为相应的页面已标记 缺席或禁止。
当通过某个进程的系统调用来调用内核时,内核代码必须在进程的地址空间内运行。所以内核代码必须在每个进程的地址空间某处(但保护:在MMU表防止未经授权的用户代码访问内核内存)。 由于代码可以包含硬编码的地址,因此对于所有进程,内核最好都位于同一地址。 通常,在Linux中,该地址为0xC0000000。 每个进程的MMU表将地址空间的该部分映射到内核在引导时实际加载的任何物理RAM块。 请注意,内核内存永远不会被交换掉(如果可以交换空间中读取数据的代码本身被交换掉了,事情就会很快变坏)。
在PC上,事情可能会有些复杂,因为有32位和64位模式,段寄存器和PAE(充当具有大量页面的第二级MMU)。 基本概念保持不变:每个进程都有自己的4 GB虚拟地址空间视图,内核使用MMU将每个虚拟页映射到RAM中的适当物理位置,或者根本不映射。