[英]Understanding fork() order in C
所以我有一个我想理解的程序,它来自旧的考试,但是我无法掌握。 我如何知道分叉的顺序以及如何更改变量?
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;
}
对fork()
的调用既启动了新进程,又继续了旧进程。 如果存在某种错误,它将返回一个错误值。 所有错误和仅有错误是负数。 这是第一个if
块检查的内容。
在新进程中, fork()
返回0。因此,仅在子进程中调用递增v
并在g
递减的分支,而不调用父进程。
在原始进程中, fork()
函数返回子进程的进程标识符(PID),它是一个正整数。 (这将稍后传递给waitpid()
。因此,仅在父进程中调用递减v
而在g
递增的分支,而不在子进程中调用。
每个进程都有其自己的v
和g
副本。 (这是进程和线程之间的主要区别:线程共享内存。)在现代SMP操作系统上,将发生的事情是子进程获得父进程的内存映射的副本。 但是这些引用指的是物理内存的相同页面,直到一个进程或另一个进程对其进行写入为止。 发生这种情况时,将为该内存页面创建一个副本,并且两个进程现在都将获得自己的不同副本。
现代Linux内核实现fork()
,子进程将在父进程之前继续执行。 这对性能产生了重大影响。 大多数调用fork()
程序都会立即有子进程调用exec()
来启动新程序。 这意味着它根本不需要父代内存的副本。 (现在,有一种更新,更简单的方法可以在新进程中启动另一个程序posix_spawn()
。)另一方面,父进程几乎总是保持运行并修改其内存。 因此,给孩子一个机会来声明将要丢弃它继承的内存,这意味着父母无需担心为其孩子留下任何内存页的未修改副本,并且内核也不必经历写时复制的严格条件。
但是实际上,任何体面的编译器都会将两个局部变量都保留在寄存器中,因此不会出现此问题。
在循环的下一个迭代中(仅在子进程终止之后才发生),使用父变量的更新值生成新的子进程。 每个子进程还将继续使用其从其父级继承的v
和g
值运行循环。
每次对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到达其waitpid的末尾,退出循环并打印其结果。
此时,父级将解除阻止,再循环一次(沿途分叉另一个孩子),然后(一旦孩子完成)退出循环。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.