所以我有一个我想要理解的程序,它来自一个旧的考试,但我就是不能掌握它。我如何知道fork的顺序以及变量是如何改变的?
static int g = -1;
int main(int argc, char *argv[])
{
int v = 0;
pid_t p;
while (v++ < 6)
if ((p = fork()) < 0) {
perror("fork error");
return 1;
} else if (p == 0) {
v++;
g--;
} else {
g++;
v+=3;
if (waitpid(p, NULL, 0) != p) {
perror("waitpid error");
return 1;
}
}
printf("mypid = %d parentpid = %d p = %d v = %d g = %d\n",
getpid(), getppid(), p, v, g);
return 0;
}
发布于 2018-12-11 07:09:52
对fork()
的调用既会启动一个新进程,又会继续执行旧进程。如果有某种错误,它会返回一个error值。所有错误和只有错误都是负数。这是第一个if
块检查的内容。
在新进程中,fork()
返回0。因此,递增v
和递减g
的分支仅在子进程中调用,而不是在父进程中调用。
在原始进程中,fork()
函数返回子进程的进程标识符(PID),它是一个正整数。(这将在稍后传递给waitpid()
。因此,只在父进程中调用递减v
和递增g
的分支,而不是子进程。
每个进程都有自己的v
和g
副本。(这是进程和线程之间的主要区别:线程共享内存。)在现代的SMP操作系统上,子进程将获得父进程的内存映射的副本。但它们指的是相同的物理内存页面,直到一个进程或另一个进程写入它们。当发生这种情况时,将对该页内存进行复制,两个进程现在都会获得自己的、不同的副本。
按照现代Linux内核实现fork()
的方式,子进程将在父进程之前继续。这对性能有很大的影响。大多数调用fork()
的程序都会立即让子进程调用exec()
来启动新程序。这意味着它根本不需要父内存的副本。(现在有一种更新、更简单的方法可以在新进程中启动不同的程序,即posix_spawn()
。)另一方面,父进程几乎总是保持运行并修改其内存。因此,让子进程有机会声明它将丢弃继承的内存,这意味着父进程不需要担心为其子进程留下任何内存页面的未经修改的副本,内核也不必经历写时复制的繁琐过程。
但在实践中,任何像样的编译器都会将这两个局部变量都保存在寄存器中,因此不会出现这个问题。
在循环的下一次迭代中(仅在子进程终止后发生),将使用父进程变量的更新值生成一个新子进程。每个子进程还使用从其父进程继承的相同的v
和g
值继续运行循环。
发布于 2018-12-11 07:19:56
每个对fork的调用都会使用自己的变量生成自己的进程,这些变量会在调用时被复制(逻辑上,当实际复制发生时,优化可能会发生变化,但不会改变结果)。
所以当你进入循环时,v递增到1,然后你分叉。此时,父进程具有g=-1,v=1,p=,而新子进程具有g=-1,v=1,p=0。然后,父进程进入else情况,将g增加到0,v增加到4,然后等待子进程完成,而子进程进入"else if (p == 0)",将v增加到2,将g减少到-2,然后再次循环。
至此,您希望已经获得了足够的信息来遵循下两个子进程的逻辑,完成循环,并打印各自的结果。当它们这样做时,第一个子进程也将使用v=6结束其等待进程,退出循环,并打印其结果。
此时,父对象将解除阻塞,再次循环(在此过程中再派生一个子对象),然后(该子对象完成后)退出循环。
https://stackoverflow.com/questions/53714886
复制相似问题