在 x86 CPU 中,有编号为 0 的控制寄存器。该寄存器的第 16 位表示“写保护”设置。如果该位被清除,CPU 能够覆盖只读数据。(在页表条目中配置)在内存中。如果该位被设置,CPU 不能覆盖内存中的 RO 数据。
我很好奇的是“这个位的初衷是什么??” “为什么 x86 CPU 需要这个??”
在 x86 CPU 中,有编号为 0 的控制寄存器。该寄存器的第 16 位表示“写保护”设置。如果该位被清除,CPU 能够覆盖只读数据。(在页表条目中配置)在内存中。如果该位被设置,CPU 不能覆盖内存中的 RO 数据。
我很好奇的是“这个位的初衷是什么??” “为什么 x86 CPU 需要这个??”
引用英特尔® 64 和 IA-32 架构软件开发人员手册第 3A 卷第 页。2-15(强调我的):
WP 写保护(CR0 的第 16 位)— 设置时,禁止管理员级程序写入只读页面;当清零时,允许管理员级程序写入只读页面(无论 U/S 位设置如何;参见第 4.1.3 节和第 4.6 节)。此标志有助于实现创建由操作系统(如 UNIX)使用的新进程(分叉)的写时复制方法。
更新:查看关于 fork() 的维基百科:
每当进程(父进程或子进程)修改页面时,都会为执行修改的进程(父进程或子进程)单独制作该特定页面的单独副本。
这是写入时复制的核心,但在内核完成修改时会出现问题(例如当写入是由于 syscall - think 发生时read()
)。
从4.1.3开始:
CR0.WP 允许保护页面免受管理员模式写入。如果 CR0.WP = 0,则允许对具有只读访问权限的线性地址进行超级模式写访问;如果 CR0.WP = 1,则不是。(无论 CR0.WP 的值如何,都不允许用户模式写访问具有只读访问权限的线性地址。)
通过设置CR0.WP = 1
内核将在修改只读用户页面时收到通知(带有页面错误),并且可以在继续页面修改之前执行写时复制操作。
WP=1 是默认值。它在内核写入只读(写保护)用户(U)页面时启用陷阱,因此内核可以检查它是否是 COW 页面(因此它知道需要创建另一个不是 COW 的页面副本) ,否则它将不得不检查它正在访问的每个内存地址,否则它会通过使更改对映射内存的其他进程可见而破坏 COW。
WP 可以设置为 0(但它希望确保在此处理期间禁用/屏蔽中断,以便控制不能被不知道此条件的代码执行带走)。这允许写保护的 cr3 页面被取消写保护(因为当您将其递归 PML4 条目中的写位取消设置为自身时,在您 WP = 0 之前无法对其进行任何操作,否则它将继续导致陷阱)