关于fork系统调用和全局变量

3

我有一个用C++编写的程序,它会派生出两个新进程:

#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdlib>
using namespace std;

int shared;

void func(){
  extern int shared;
  for (int i=0; i<10;i++)
        shared++;
  cout<<"Process "<<getpid()<<", shared "
        <<shared<<", &shared "
        <<&shared<<endl;
}

int main(){
  extern int shared;
  pid_t p1,p2;
  int status;
  shared=0;
  if ((p1=fork())==0) {func();exit(0);};
  if ((p2=fork())==0) {func();exit(0);};
  for(int i=0;i<10;i++)
        shared++;
  waitpid(p1,&status,0);
  waitpid(p2,&status,0);;
  cout<<"shared variable is: "<<shared<<endl;
  cout<<"Process "<<getpid()<<", shared "
        <<shared<<", &shared "
        <<&shared<<endl;
}

这两个分叉进程对共享变量进行了增量操作,父进程也做了同样的操作。由于该变量属于每个进程的数据段,因此增量是独立的,最终值为10。

然而,共享变量的内存地址是相同的,您可以尝试编译并查看程序的输出。这是为什么呢?我无法理解,我原以为自己知道fork()的工作原理,但这似乎非常奇怪...

我需要解释一下为什么地址相同,尽管它们是不同的变量。


记住fork会进行写时复制,因此除非您更改变量,否则它在所有进程中都是相同的。 - Lodle
不这样做的话,否则在分叉后您的指针将无效。并不是很有用... - Eugene
5个回答

12
操作系统正在使用虚拟内存和类似技术,以确保每个进程在相同的地址处看到不同的内存单元(虚拟或只读);默认情况下所有内存都是独立的,只有显式共享的内存(例如通过shm)才是共享的。

8

这被称为“虚拟地址”。每个进程都有自己的地址空间,每个地址的含义取决于进程。fork()创建一个副本,而不是共享数据(技术上,它们可能会得到共享的写时复制,但这与预先复制具有相同的效果)。也就是说,变量“shared”在进程之间不共享。


4

现代系统中的指针并不对应于实际的硬件内存地址。相反,这些地址映射到由操作系统管理的虚拟空间。因此,两个不同进程的指针地址可能看起来相同,但实际上并非如此。


0

是的,你所想的是对的,但为了节省一些内存,页面在父进程和子进程之间共享,直到子进程执行exec系统调用或修改任何变量或数据结构。由于许多页面在父进程和子进程之间共享......如果页面被子进程修改,则该页面将被复制到单独的内存区域并分配给子进程。如果它被父进程修改,则该页面将被复制到单独的内存区域并分配给父进程。


0

这是否也适用于 pthread_mutex 对象?比如,我在父进程中有一个互斥锁,在特定函数中进行了锁定和解锁。现在父进程创建了一个子进程。父进程和子进程都可以同时调用此函数(父进程并不真正阻塞直到子进程退出,因为父进程是多线程程序,只有一个线程会被阻塞)。 那么,这个互斥锁对象的状态——在这两个进程之间是共享的吗? 如果在父进程中, 互斥锁被锁定 然后创建子进程 子进程先运行 子进程看到互斥锁处于锁定状态,因为它刚从父进程继承了互斥锁对象 现在子进程解锁互斥锁 那么,该互斥锁在父进程中的状态是仍然锁定(因为父进程从未解锁),还是已解锁(因为子进程已经解锁)。

从父进程和子进程同时调用对全局互斥锁进行锁定/解锁的函数是否可行?


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