为什么0xfffffffc是一个无效的访问地址?

amj*_*jad 0 assembly cpu-architecture y86

嗨,我正在阅读一本教科书,它说程序不允许访问大于的地址0xc0000000(如32位版本的Linux的情况),因此汇编代码无效:

1. irmovl $1,%eax
2. xorl %esp,%esp // Set stack pointer to 0 and CC to 100
3. pushl %eax    // Attempt to write to 0xfffffffc, will fail
Run Code Online (Sandbox Code Playgroud)

我糊涂了.我有两个问题:

  1. 为什么程序不允许访问大于的地址0xc0000000,是不是像0xc0000008有效地址那样的地址?

  2. 如果真的不允许程序访问大于0xc0000000,0xfffffffc低于(小于)的地址0xc0000000,那么它为什么会失败?

Jon*_*art 5

  1. 为什么程序不允许访问大于0xc0000000的地址,是不是像0xc0000008这样的地址有效地址?

现代操作系统利用硬件的功能来防止运行的应用程序相互干扰.此隔离的主要组件是权限分离虚拟内存.

虚拟存储器(与物理存储器相对)意味着代码访问的地址(例如mov指令)不是RAM的实际地址.相反,内存管理单元(MMU)充分利用了页表翻译将其发送给RAM之前的虚拟地址(VA)到物理地址(PA).

权限分离由CPU(和MMU)强制执行,允许单个操作系统内核完全控制硬件,同时安全地运行多个用户应用程序.

将这两个概念放在一起,通常内核在虚拟内存的一个区域中运行(用户空间不可访问),而用户空间进程在另一个区域中运行.

在Linux内核的x86 32位arch端口中,经常使用1-3分割,为内核提供1 GB的VA空间,为每个用户应用程序留下3 GB的VA空间.从而:

  • 0x00000000 - 0xBFFFFFFF:用户空间
  • 0xC0000000 - 0xFFFFFFFF:内核
  1. 如果真的不允许程序访问大于0xc0000000的地址,0xfffffffc低于(小于)0xc0000000,那么它为什么会失败?

数据的表示方式(例如在硬件寄存器或内存中)和解释方式之间存在很大差异(例如有符号/无符号整数,浮点数,文本字符串,图像等)

请注意,如果 0xFFFFFFFF 解释为32位二进制补码(带符号)整数,则得到-1.如果将其解释为无符号整数,则得到(2 ^ 32 - 1)= 4294967295.

地址总是无符号的; 一般来说,当它涉及硬件时没有负数.

0xFFFFFFFC大于0xC0000000.因此,这里是一个内核地址,任何试图访问它的用户空间应用程序都会出错并传递一个SIGSEGV信号.

  • 对于在真正的现代 x86 Linux 系统上进行此实验的任何人请注意:64 位内核下的 32 位进程可以使用所有 4 GiB 虚拟地址空间,除了低 64k(默认情况下保留,因此 NULL 指针解引用错误)默默地成功:https://wiki.debian.org/mmap_min_addr),以及高 2 页。[为什么我不能在 64 位内核上 mmap(MAP\_FIXED) 32 位 Linux 进程中的最高虚拟页?](/sf/ask/3339875171/)。) 1:3或 2:2 分割仅适用于运行 32 位内核以及 32 位用户空间或 64 位进程 + 内核时。 (2认同)