在LInux多进程编程中,对于fork(), 相信很多人都不陌生,近来在复习IPC的时候发现了一个好玩的代码,现将其摘录下来,并分析之。
#include <stdio.h>
#include <unistd.h>
int a = 10;
int main(int argc, char *argv[])
{
int i = 0;
for(i = 0; i<2; i++)
{
pid_t fpid = fork(); // call once return twice.
if(fpid == 0)
printf("in child, i=%d pid =%d,ppid=%d,fpid=%d\n",i,getpid(),getppid(),fpid);
else
printf("in parent,i=%d pid =%d,ppid=%d,fpid=%d\n",i,getpid(),getppid(),fpid);
}
wait();
}
该小代码的输出为:
0 in parent,i=0 pid =6015,ppid=5043,fpid=6016
1 in child, i=0 pid =6016,ppid=6015,fpid=0
2 in parent,i=1 pid =6015,ppid=5043,fpid=6017
3 in child, i=1 pid =6017,ppid=6015,fpid=0
4 in parent,i=1 pid =6016,ppid=6015,fpid=6018
5 in child, i=1 pid =6018,ppid=6016,fpid=0
我们来分析一下这段代码的输出。
首先我们需要复习一下fork 函数的作用与特性。
在Linux系统中, fork用来将当前进程作一次完整的拷贝,并执行,fork函数本身只被调用一次,但却返回两次,在父进程中返回的是新进程的pid,而在新创建出来的子进程中,它返回0,因此我们可以用不同的返回值来实现父进程与子进程不同的行为。
那么于上面那一段代码,它是如何得出如下输入的呢?
bash[5043]
---- main[6015]
----> fork()---- main*[6016]
| #line 0 |
| #line 1
| ----> fork() ---- main***[6018]
| # line 4 |
|#line 5
----> fork() ---- main**[6017]
| #line 2 |
| #line 3
在main中,循环两次,i的值分别为0和1, 分别产生main*与main**. 这对应于line 0 和 line2的输出。
在main*中,它的代码与main的代码是一样的,此时 i=0, 只不过它是从fork()之后开始执行,此时fork()的返回值为0, 因此有了line 1的输出。然后在下一次循环的过程中产生main***,并输出line 4.
在main**中,它的代码与main的代码也是一样的,此时 i=1, 那么它在执行完输出之后就退出循环了,它只有一个输出line 3.
在main***中,它的代码与main的代码也是一样的,此时i=1, 那么它在执行完输出之后就退出循还了,它只有一个输出line 5.
输出的顺序其实是不确定的,因为在不同的时刻,哪一个进程优先调度是由内核来管理的,而它的调度行为我们是没办法确定的。
当我们在看一段代码的时候,我们首先要对代码中的关键函数的行为有一个比较深刻的理解,才能够判断出函数的具体行为。我们在学习的时候很容易遇到一些看似简单,实则并不那么容易的问题,它们或许输出的结果与我们的设想大相径庭,这个时候我们要做的就是静下心来好好想想,我们真正理解每个细节吗?