问题描述
由于 COW,linux 只会在子进程写入页面后才将其分配给子进程。它还只会在更改后为堆栈分配一个新页面。因此,例如,如果在 fork()
系统调用之后,我们在子进程中调用 printf,由于堆栈已更改,我们将出现页面错误。
但是我不确定以下代码:
fork();
char *arr = mmap(... some args.. MAP_FILED|MAP_PRIVATE);
由于父子俩都运行 mmap,我假设第二行会发生页面错误,因为我们调用了一个函数(实际上是系统调用)并创建了一个局部变量,因此更改了堆栈。这是正确的吗?
TL;博士:
我们 fork 后 mmap 是否会导致页面错误?
解决方法
调用mmap()
,或者任何函数,都会导致栈上的页面错误,因为它需要在栈上保存返回地址,然后为被调用的函数创建一个新的栈帧。
即使是对局部变量的赋值也会在没有函数调用的情况下导致页面错误。所以简单地做:
fork();
char *arr = NULL;
可能会导致页面错误(我说“可能”是因为编译器可能会优化它以在函数调用之前初始化内存一次;此外,arr
变量可以存储在寄存器中而不是内存中) .
大多数使用 fork()
的代码将返回值保存在一个变量中,因此它知道它是在父变量中还是在子变量中。所以通常只要 fork()
返回就会出现页面错误(同样,除非该变量使用了寄存器)。
在所有这些情况下,只会在父进程或子进程中出现错误,而不是两者都有。第一个修改栈帧的进行复制,然后清除COW标志,这样其他进程就可以继续使用原来的页面了。
,页面错误发生在mmap
返回后,您开始使用分配的段。在此之前,内核会检查错误,并将资源分配给您(以管理方式),但在您发出实际请求之前,它实际上不会将它们分配给您(这是当您通过读取或写入访问内存本身时)内存)这遵循延迟初始化的技术,它使进程的启动成为一个更顺畅的任务,并且不会不必要地分配最终未被进程使用的资源。