fork()是什么?
fork()的原理其实非常简单:当你调用fork()时,会产生一个子进程,这个子进程会拥有父进程所有的东西(记住,是所有)更通俗一点来讲,在函数代码段中,调用fork后,调用一次,返回两次,如果返回0,则代表当前的进程是子进程,不是的话则是父进程。接下来我们看段代码深入理解一下:
int main(int argc, char *argv[])
{
pid_t pid;
int x = 1;
pid = Fork(); //line:ecf:forkreturn
if (pid == 0) {
/* Child */
printf("child : x=%d\n", ++x);
fflush(stdout);
return 0;
}
/* Parent */
printf("parent: x=%d\n", --x);
fflush(stdout);
return 0;
}
代码中先定义了x=1,之后调用了fork并获取返回值,它判断了pid的值,如果等于0,代表是子进程,则输出child:x= ,如果是父进程则输出parent:x= ,在这里要注意两个进程是相互独立互不干扰的,所以每一次的输出结果可能都会不一样,我本次输出的是:
- child:x=2
- parent: x=0
如答案所示:判断pid等于0,则是子进程输出x加一之后的值,之后则退出,return 0直接为退出程序,而到了父进程的时候,x仍为1,则父进程输出x减一之后的值。怎么样,看到现在是不是有点明白原理了呢?接下来一大波实验即将来袭!
实验一
void fork1()
{
if (fork() == 0) {
printf("Hello from child\n");
}
else {
printf("Hello from parent\n");
}
}
int main(){
fork1();
}
这个是很简单的例子,如果是子进程则输出Hello from child,父进程则输出Hello from parent。
则输出结果可能是以下可能:
- Hello from child
Hello from parent - Hello from parent
Hello from child
还有一点不知道有没有细心的小伙伴注意到,上面实验中有一个fflush(stdout),而这个实验中没有,而是换行符。
首先呢,对于一个shell中运行的程序,默认的标准输出是控制台,而控制台是行缓冲的,也就是系统在缓冲区看到换行符,就帮你冲洗缓冲区。如果调用fflush,也可以触发输出,这就是强制冲洗。因此,如果只写输出语句不加换行,屏幕上是不会有语句的哦~不仅不会,有的时候还会产生令你满头问号的结果,因此,养成一个良好的习惯,从现在做起!
实验二
void fork2()
{
int x = 1;
pid_t pid = fork();
if (pid == 0) {
printf("Child has x = %d\n", ++x);
}
else {
printf("Parent has x = %d\n", --x);
}
printf("Bye from process %d with x = %d\n", getpid(), x);
}
int main(){
fork2();
}
可以看到运行结果,调用fork后,子进程拥有父进程所有代码,所以printf也要执行,即如上图所示,也可以观察到子进程和父进程的进程号。
实验三
void fork3()
{
printf("L0\n");
fork();
printf("L1\n");
fork();
printf("Bye\n");
}
int main(){
fork3();
}
好的,我估计到这里就有人开始头冒问号了,这里我用最通俗的语言讲一下,不然再看下去的小伙伴就会秃头了!
大家可以在本子上画一个进程图,就会很清晰
我们可以从图中看出大体结构,打印完L0只后,调用了fork返回两个进程父进程继续往下走,打印L1,调用fork又返回两个进程,父进程继续往下走,打印bye,而第一个fork的子进程只会继承fork之后的代码,因此打印L1后继续调用fork,再产生两个子进程,只后如上,但因为所有的父进程和子进程都是相互独立的(就是当前干嘛完全是计算机想干啥干啥,反复横跳,但要记住,要有bye必须得先有L1,要想有L1,必须得先有L0)所以输出可能性就有很多种,有啥可能性呢?如下(这里就不换行了 麻烦):
- L0 L1 Bye L1 Bye Bye Bye
- L0 L1 L1 Bye Bye Bye Bye
- L0 L1 Bye Bye L1 Bye Bye
好了,你有没有列出来呢,接下来难度升级哦,准备好!
实验四
void fork4()
{
printf("L0\n");
fork();
printf("L1\n");
fork();
printf("L2\n");
fork();
printf("Bye\n");
}
int main(){
fork4();
}
好的,我们可以数一数一共有一个L0,两个L1,四个L2,8个Bye,但因为进程独立,所以输出方式又有很多种啦,上面只是其中之一,列举如下的可能性(太多啦,只列举一些,大家只要记住上面的规则就可以都推出来的,完全是数学的排列组合问题嘛,o(╥﹏╥)o):
- L0 L1 L2 Bye Bye L1 L2 Bye Bye L2 Bye Bye L2 Bye Bye
- L0 L1 L2 Bye L1 Bye L2 L2 L2 Bye Bye Bye Bye Bye Bye
- L0 L1 L2 Bye L1 Bye L2 Bye Bye L2 Bye Bye L2 Bye Bye
- L0 L1 L2 Bye L1 Bye L2 Bye Bye L2 L2 Bye Bye Bye Bye
- L0 L1 L2 L2 Bye Bye Bye Bye L1 L2 L2 Bye Bye Bye Bye
实验五
void fork5()
{
printf("L0\n");
if