为什么 execve 函数映射私有区域?

why execve function map private areas?

我正在阅读一本教科书,其中描述了 execve 函数:

假设当前进程中的程序运行ning进行如下调用:

execve("a.out", NULL, NULL);

execve 函数加载并运行s 包含在当前进程中的可执行对象文件a.out 中的程序,有效地用a.out 程序替换当前程序。正在加载和 运行ning a.out 需要以下步骤

  1. 删除现有用户区
  2. 映射私人区域。为代码、数据、bss 和堆栈创建新的区域结构 新计划的领域。所有这些新区域都是 private copy-on-write as:

  1. 映射共享区域
  2. 设置程序计数器

我对步骤 2 有点困惑,所以假设 fork() 分叉一个子进程并让子进程 运行 execve 用于新程序,那么为什么 execve 将子进程的新区域映射为私有写时复制?父进程不会与子进程共享内存,因为父进程和子进程映射到不同的对象,为什么子进程害怕其他进程可能写东西影响它?

如果您查看图表,大多数“私人 copy-on-write”页面都是“demand-zero”页面。这就是 Linux 处理分配请求的方式;它 copy-on-write 映射零页。因此,如果您只是阅读该页面,您会看到它充满了零,但此时它并没有占用实际内存。一旦您需要写入它,它就会无缝地创建一个新的“真实”页面并用零填充并将您的写入应用到它。

对于其余部分,如 .data 部分,它映射到可执行文件的原始数据中,但您不想修改磁盘上的文件,因为您重新分配了一个全局变量,所以再次使用 copy-on-write。

基本上,它不是从 原始 程序中“复制”真实页面,它只是一种策略,用于从 OS 中向程序提供始终如一的清零页面,并以允许在需要时进行修改的方式廉价地提供对原始可执行文件数据的访问。

在此上下文中,私有映射与共享映射相对。文件的可写共享映射将需要对该文件的写访问权,并且对它的任何更改都将反映在磁盘和文件的所有其他共享映射中,包括同一程序或库的其他实例。这显然是不希望的。

这是可执行文件 file/libraries 的映射。对于像 bss 这样没有文件支持(“匿名”映射)的东西,您可能想知道为什么不能使用共享映射。事实上它会直到进程分叉,但如果你分叉,共享的匿名映射将在父进程和子进程之间共享。有时你想对你自己用 mmap 创建的映射有意这样做,但它没有为 fork 后的全局数据对象提供正确的语义,所以它不能在这里使用。