本文要讲解以下内容:
1.程序地址空间
2.fork() / vfork()函数
(1.)首先我们看一下C语言的内存空间分布图:
(2.)接下来我们更加深度的理解以下进程的地址空间
1 #include
2 #include
3 #include
4 #include
5
6 int g_val = 0;
7
8 int main()
9 {
10 pid_t id = fork();
11 if(id<0)
12 {
13 perror("fork");
14 return -1;
15 }
16 else if(id == 0)
17 {
18 printf("child[%d]: %d : %p\n",getpid(),g_val,&g_val);
19 }
20 else
21 {
22 printf("parent[%d]: %d : %p\n",getpid(),g_val,&g_val);
23 }
24
25 sleep(1);
26 return 0;
27 }
更具上边的代码以及运行结果我们可以看出,父子进程的id不同,但是定义的全局变量g_val的结果和地址是相同的。这是由于子进程是按照父进为模板拷贝的,如果父进程不做任何修改那么子进程也不会有变化
接下来我们对上边代码做一定的修改:
1 #include
2 #include
3 #include
4 #include
5
6 int g_val = 0;
7 int main()
8 {
9 pid_t id = fork();
10 if(id<0)
11 {
12 perror("fork");
13 return -1;
14 }
15 else if(id == 0)
16 {
17 g_val = 100;
18 printf("child[%d]: %d : %p\n",getpid(),g_val,&g_val);
19 }
20 else
21 {
22 printf("parent[%d]: %d : %p\n",getpid(),g_val,&g_val);
23 }
24
25 sleep(1);
26 return 0;
27 }
我们可以看到输出的全局变量的地址一样但是内容却不一样,这就很神奇,那么我们可以初步得出以下结论:
下边我们用图来解释进程的地址空间
注意:
上面的图就足以说明,同一个变量只是虚拟地址相同,但是内容通过页表被映射到物理地址空间的不同位置,这也就很好的解释了上边那段代码为何地址相同,值不同
1.首先利用man fork 来认识fork函数
总结:
什么是写时拷贝??
通常,父子代码共享,父子不在写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式进行,请见下图:
2.我们利用fork函数的返回值来进行分流,将父子进程分开
1 #include
2 #include
3 #include
4 int main()
5 {
6 int ret = fork();
7 if(ret < 0)
8 {
9 perror("fork");
10 return -1;
11 }
12 else if(ret == 0)
13 {
14 printf("I am child: %d! , ret: %d\n",getpid(),ret);
15 }
16 else
17 {
18 printf("I am father: %d! , ret: %d\n",getpid(),ret);
19 }
20 sleep(1);
21 return 0;
22 }
我们可以看出我们不仅将父子进程进行了分流还验证了父子进程的返回值
3.进程调用fork,当控制转移到内核中的fork代码后,内核做:
4.当一个进程调度fork之后,就有两个二进制代码相同的进程。而且他们运行到相同的地方,但是他们都是各自运行互不干扰,看下边代码:
1 #include
2 #include
3 #include
4 #include
5 int main()
6 {
7 pid_t pid;
8
9 printf("Before:pid is %d\n",getpid());
10 if((pid = fork()) < 0)
11 {
12 perror("fork");
13 exit(1);
14 }
15 printf("After:pid is %d ,fork return %d\n",getpid(),pid);
16 sleep(1);
17 return 0;
18 }
我们可以看到在打印了三行代码一行fork之前,两行fork之后,那么为什么在fork之后没有打印fork之前的pid尼???
接下来我们用图解告诉大家
所以我们可以看到fork之前父进程独立执行,fork之后,父子进程两个执行流分别执行。
注意:fork之后,谁先执行完全由调度器决定
5.fork常规用法
6.fork调用失败的原因
vfork的返回值以及创建失败的返回值和fork一样,这里就不做详细说明了
注意:
下边是vfork的使用程序:
2 #include
3 #include
4 #include
5 int ret = 100;
6 int main()
7 {
8 pid_t pid;
9
10 if((pid = vfork()) < 0)
11 {
12 perror("fork");
13 exit(1);
14 }
15 else if(pid == 0)
16 {
17 sleep(5);
18 ret = 200;
19 printf("child ret: %d\n",ret);
20 exit(0);
21 }
22 else
23 {
24 printf("parent ret: %d\n",ret);
25 }
26
27 return 0;
28 }
根据上边的结果我们可以看子进程改变了父进程的变量值,因此子进程在父进程的地址空间中运行
2.fork和vfork的区别