当存在全局指针时,为什么分叉进程不会相互影响?

3
我知道fork()函数创建一个和父进程相同的进程,只有PID不同。它们最初具有相同的变量,并且对这些变量所做的更改不会互相影响。但是当全局指针变量被共享时会发生什么?
我编写了一些代码并打印了结果。看起来父进程和子进程都指向相同的内存位置,然而在这些内存位置上所做的更改(例如父进程中的*p = 1和子进程中的*p = 2)不会相互影响。还要注意,我让父进程wait(NULL)直到子进程退出。因此,子进程更改了指向与父进程指针相同内存地址的指针所指向的值。
我知道当调用fork()时,父进程克隆了所有内容:寄存器、程序计数器等等。但这是如何实现的呢?难道子进程退出后,父进程的变量值不应该已经发生了变化吗?这是因为系统将所有内容(包括父进程的指针变量)放入栈中,并在子进程终止时弹出它们吗?

6
每个进程都有自己的虚拟内存映射 - 地址是“虚拟的”。 - teppic
阅读一些关于Unix或Linux编程的好书,比如http://advancedlinuxprogramming.com/,并了解更多关于http://en.wikipedia.org/wiki/Virtual_memory以及例如`mmap(2)`、`fork(2)`和`execve(2)`系统调用的知识。 - Basile Starynkevitch
3
我知道当调用fork()时,进程会共享所有东西,包括寄存器、程序计数器等等。--- 注意,你知道的有些事情是不正确的。 - n. m.
它(fork)创建了原始进程的完全副本,包括所有文件描述符、寄存器——一切。安德鲁·坦尼鲍姆 - 操作系统设计与实现。当我说“共享”时,实际上是指克隆,如果你认为我知道不正确的事情。它们不是“共享”,而是被克隆到子处理器中。那就是我的意思。 - Varaquilex
1
@Volkanİlbeyli 对的,这是一份复制品,不是共享的。 - nos
2个回答

4
当一个进程被分叉时,新的进程在所有意义上都是原始进程的一个 副本,具有自己的虚拟地址空间、文件描述符等。从比较简单的角度来看,同样的内存地址实际上将为每个进程指向不同的物理内存地址 - 你可以有两个相等的指针指向完全不同的数据。

当然,在现代操作系统中事情并不那么简单。fork(),例如,实际上并没有复制所有东西,因为这将浪费处理器时间和内存。内核利用一些页面表操作来实现写时复制内存复制。此外,为了性能和正确性的原因,可以在某种程度上控制哪些资源将实际克隆到子进程中。


2
这是一个非常大的话题,但以下是一个简要解释。你可能会将执行线程与分叉进程混淆。在Unix系统中,每个进程都是独立的,并且具有自己的虚拟地址空间。这意味着,将指针设置为相同地址的两个进程通常实际上在使用物理内存中不同的地址。这个概念是任何优秀操作系统的重要组成部分,在更原始的操作系统上,运行的程序将直接访问物理内存。事实上,如果你同时运行一千个可执行文件实例,你可能会发现每个实例都在使用完全相同的地址来执行所有操作。
除了安全性的好处之外,这还允许程序的“文本”部分(二进制指令)被所有运行程序的实例共享。内核可以将每个进程映射到物理内存中的相同地址。
孩子并不是父进程的精确副本,也不是完全共享所有内容。有很长的一份清单,列出了哪些是继承的,哪些不是。它基本上是相同的 - 它具有相同的可执行代码,相同的打开文件等等,但它是一个完全独立的、单独的进程。

老实说,我还没有学过线程,所以我不会把它们弄混:) 感谢您提供详细的回答,加一分!但另一个答案才是我要找的,因此我接受了那个答案。 - Varaquilex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接