fork作用
fork可以用来创建一个新的进程。
fork声明
fork特性
- fork调用一次,返回两次:
1)返回0给子进程
2)返回子进程的进程ID给父进程 - 父子进程的先后执行顺序是不确定的,这个取决于内核的调度算法
- 子进程只是父进程的一份拷贝,子进程会复制父进程的数据空间、堆、栈。它们不会共享这部分内容,只会共享正文段(正文段,可以把它理解成一块专门存储代码的内存)
下面通过三个例程来说明fork的特性
demo1
1 void demo1(void)
2 {
3 pid_t pid;
4 int val = 0;
5
6 if( (pid=fork()) < 0)
7 {
8 printf("fork fail\n");
9 }
10 else if(0 == pid) // This is children process
11 {
12 val = 50;
13 printf("This is children, parent pid:%d, current pid:%d, pid:%d\n", getppid(), getpid(), pid);
14 }
15 else // This is parent process
16 {
17 val = 100;
18 printf("This is parent, parent pid:%d, current pid:%d, pid:%d\n", getppid(), getpid(), pid);
19 }
20
21 printf("pid:%d, val:%d\n", getpid(), val);
22 }
程序运行的结果如下
This is parent, parent pid:2908, current pid:3164, pid:3165
pid:3164, val:100
This is children, parent pid:1, current pid:3165, pid:0
pid:3165, val:50
分析demo1的代码和运行结果
从运行结果看,第13行的打印输出了,而且第18行的打印输出了,平时我们看到的code,一般if和else只会跑其中一个,但是这里跑了两次。说明fork调用一次,返回两次,所以else if(0 == pid)和else这两种情况都会跑进去。
从代码和运行结果看,打印显示父进程中pid:3165,子进程中current pid:3165, pid:0 ,fork之后,有两个返回值,返回0给子进程,返回子进程的ID给父进程。
运行结果显示的是先打印parent,再打印children,但是这个打印是不确定的,有可能是先打印children,再打印parent。有的人可能会想,如果我在parent打印前加个sleep(1)或者spleep多秒,这样是不是就保证了children会先打印呢?这个不好说,即使在parent打印前sleep多秒,系统还是有可能先打印children,这个要根据系统的调度算法。
从运行结果看,parent和children的val值都是自己最先设定的值,这说明了子进程只是父进程的一个拷贝,子进程将父进程的数据空间、堆、栈都拷贝了一份,而不是共享。所以这里val的值大家互不干涉。
demo2
1 void demo2(void)
2 {
3 printf("l1\n");
4 fork();
5 printf("l2\n");
6 fork();
7 printf("l3\n");
8 }
程序运行的结果如下
l1
l2
l3
l2
l3
l3
l3
分析demo2的代码和运行结果
程序跑到第3行的时候,会打印出l1
当程序跑到第4行fork之后,会创建出一个父进程A和一个子进程B,因为子进程是父进程的一份拷贝。也就是说,父子进程都会包含剩下的正文段(代码),即都包含第5行到第8行的代码。那么,接下来,父子进程都会跑第5行的代码,也就是各自打印出l2。
接下来,主要讲父进程A怎么跑的,因为父子进程跑的内容是一样的。程序运行到第6行的时候,又fork了一次,父进程A里又创建了一个父进程C和子进程D,父(C)子(D)进程都包含第7行代码,所以父进程(C)和子进程(D)都会打印出l3.
因为父进程A和子进程B执行的是一样的内容,所以父进程A创建的进程C和进程D,会打印出两个l3,子进程B也是一样会打印出两个l3.
可以通过下图更直观地了解,打印的顺序看具体的系统调度算法。
demo3
1 void demo3(void)
2 {
3 printf("l1\n");
4 fork();
5 printf("l2\n");
6 fork();
7 printf("l3\n");
8 fork();
9 printf("l4\n");
10 }
程序运行的结果如下
l1
l2
l3
l2
l4
l3
l4
l3
l4
l4
l4
l3
l4
l4
l4
分析demo3的代码和运行结果
其实demo3和demo2很像,刚开始都会打印出l1,遇到第一个fork,就会创建一个父进程A和子进程B,进程A和B都会拷贝第5行到第9行的代码
那么接下来父(A)子(B)进程都会打印出l2,遇到第二个fork,父(A)子(B)进程各自都会创建新的父子进程,以此类推,直接用图来表示