问题描述
假设当前进程中运行的程序进行了以下调用:
execve("a.out",NULL,NULL);
execve函数在当前进程中加载并运行可执行目标文件a.out中包含的程序,从而有效地将当前程序替换为a.out程序。加载并运行 a.out需要执行以下步骤
- 地图共享区域
- 设置程序计数器
我对步骤2感到有些困惑,因此,假设fork()
派生了一个子进程,并让子进程运行execve
来运行新程序,那么为什么execve
映射了该子进程进程的新区域为私有写时复制?父进程不会与子进程共享内存,因为父子进程将它们映射到不同的对象,为什么子进程担心其他进程可能会写某件事来影响它?
解决方法
如果您查看该图,那么大多数“写时专用复制”页面都是“零需求”页面。这就是Linux处理分配请求的方式。它写时复制映射零页。因此,如果您仅阅读该页面,就会看到它充满了零,但此时并没有占用实际内存。一旦您需要对其进行写操作,它将无缝地创建一个新的“真实”页面,其中填充有零,并将您的写操作应用于该页面。
对于其余部分,如.data
部分,它映射到可执行文件中的原始数据中,但是由于重新分配了全局变量,因此您不想修改磁盘上的文件,因此,使用写时复制。
基本上,它不是从原始程序中“复制”真实页面,它只是一种策略,可以从操作系统向该程序提供始终为零的页面,并廉价地提供对原始可执行文件的访问。数据的方式允许在需要时进行修改。
,在这种情况下,私有映射与共享映射相反。文件的可写共享映射将需要对该文件的写访问权,并且对该文件的任何更改都将反映在磁盘上以及文件的所有其他共享映射中,包括同一程序或库的其他实例。显然这是不需要的。
用于可执行文件/库的映射。对于诸如bss之类的没有文件后备的东西(“匿名”映射),您可能想知道为什么不能使用共享映射。确实要等到该过程分叉,但是如果分叉,则共享的匿名映射将在父级和子级之间共享。有时,您可能想对使用mmap
创建的地图故意这样做,但是在fork之后,它并没有为全局数据对象提供正确的语义,因此不能在此处使用。