0

我遇到了一个看起来像这样的代码

int main(void){
  pid_t pid;
  char sharedVariable='P';
  char *ptrSharedVariable=&sharedVariable;
           
  pid = fork()
  if(pid==0) {
     sharedVariable = 'C';
     print("Child Process\n");
     printf("Address is %p\n", ptrSharedVariable);
     printf("char value is %c\n", sharedVariable);
     sleep(5);
  } else {
     sleep(5);
     print("Parent Process\n");
     printf("Address is %p\n", ptrSharedVariable);
     printf("char value is %c\n", sharedVariable);
  }

根据我对堆栈溢出的了解,我可以看出父进程和子进程的 char 值会有所不同。孩子的价值是'C',父母的价值是'P'。我也可以告诉父母和孩子的地址应该是相同的,即'sharedVariable'(&sharedVariable)的地址。

但是,这是我的问题。

  1. 将不同的 char 值分配给不同的进程有什么意义?因为一方面,既然我们已经可以通过 pid==0 或 >0 来识别每个进程,那么这一步不是冗余吗?另一个原因是我认为区分两个执行相同工作的进程没有意义,如果不让程序员将它们区分开来,它们就不能工作吗?
  2. 为什么让父母和孩子的地址保持不变?我可以建议,因为假设他们继续执行类似的任务,这样做会很方便,因为这样我们就可以复制和粘贴代码。我很犹豫,想确认一下。
  3. 如果我用 vfork() 替换了 fork(),那么父级 char 值的结果会是“C”吗?

提前一百万谢谢。

4

1 回答 1

0

这个问题已经回答了好几次了。例如这里

尽管我可能会重复几个答案中已经写过的内容,但以下是您的 3 点的一些精确度:

  1. 您共享的代码中char 变量 ( ) 的命名sharedVariable令人困惑,因为该变量不在父进程和子进程之间共享。子地址空间是父地址空间的副本。因此,这里有两个进程(父进程和子进程)与上述变量所在的堆栈同时运行(一个在父堆栈中,另一个在子堆栈中)。
  2. 进程的地址空间是虚拟的。在每个进程中,您将看到相同的虚拟地址,但它们使用正确的代码和数据运行(即它们“指向”不同的物理内存位置)。在内核中进行优化以共享尽可能多的资源,直到它们被其中一个进程修改(例如,写时复制原则),但这从用户空间程序员的角度来看是透明的。
  3. 如果使用vfork(),则变量是共享的,因为地址空间在父子节点之间共享。您没有副本,因为它已为fork(). 生成的子进程就像一个协程(它比线程更轻,因为即使堆栈也是共享的!)。这就是为什么父进程被挂起,直到子进程退出或执行一个新程序。该手册警告了此类操作的风险。它的目标是立即执行一个过程(快速fork()/exec()过程)。它不是专门用于长期存在的子进程,因为对 GLIBC 或任何其他库服务的任何调用都可能失败或触发父进程中的损坏。vfork()是对系统的直接调用,没有来自 GLIBC 的任何附加值。如果是fork(),用户空间库做了很多“内务处理”以使库在父进程和子进程中都可用(GLIBC 的包装器fork()pthread_atfork()回调)。在 的情况下vfork(),这个“内务处理”没有完成,因为子进程应该被另一个程序通过execve()调用直接覆盖。这也是子进程vfork()不能调用exit()而是_exit()的原因,因为子进程会运行父进程的任何注册atexit()回调,这可能导致子进程和父进程发生意外崩溃。
于 2020-11-05T11:15:53.837 回答