进程控制天字第1号系统调用——fork
2013-09-13 20:21
232 查看
本系列文章节选自本人所著《Linux下C语言应用编程》。
本系列文章,所需代码请从以下地址下载:
http://download.csdn.net/download/scyangzhu/5129027
1.1.1 fork的机制与特性
1 #include <stdio.h>2#include <unistd.h>
3#include <stdlib.h>
4
5int main(void)
6 {
7 pid_t pid;
8 if ((pid = fork()) == 0){
9 getchar();
10 exit(0);
11 }
12 getchar();
13 }
14
父进程成功调用fork(8行)后将会产生一个子进程。此时会有2个问题:
1. 子进程的代码从哪里来?
2. 子进程首次被OS调度时,执行的第1条代码是哪条代码?
子进程的代码是父进程代码的一个完全相同拷贝。事实上不仅仅是text段,子进程的全部进程空间(包括:text/data/bss/heap/stack/command line/environment variables)都是父进程空间的一个完全拷贝。
下一个问题是:谁为子进程分配了内存空间?谁拷贝了父进程空间的内容到子进程的内存空间?fork当仁不让!事实上,查看fork实现的源代码,由4部分工作组成:首先,为子进程分配内存空间;然后,将父进程空间的全部内容拷贝到分配给子进程的内存空间;然后在内核数据结构中创建并正确初始化子进程的PCB(包括2个重要信息:子进程pid,PC的值=善后代码的第1条指令地址);最后是一段善后代码。
由于子进程的PCB已经产生,所以子进程已经出生,因此子进程就可以被OS调度到来运行。子进程首次被OS调度时,执行的第1条代码在fork内部,不过从应用程序的角度来看,子进程首次被OS调度时,执行的第1条代码是从fork返回。这就导致了fork被调用1次,却返回2次:父、子进程中各返回1次。对于应用程序员而言,最重要的是fork的2次返回值不一样,父进程返回值是子进程的pid,子进程的返回值是0。
至于子进程产生后,父、子进程谁先运行,取决于OS调度策略,应用程序员无法控制。
以上分析了fork的内部实现以及对应用程序的影响。如果应用程序员觉得难以理解的话,可以暂时抛开,只要记住3个结论即可:
1. fork函数被调用一次(在父进程中被调用),但返回两次(父、子进程中各返回一次)。两次返回的区别是子进程的返回值是0,而父进程的返回值则是子进程的进程ID。
2. 父、子进程完全一样(代码、数据),子进程从fork内部开始执行;父、子进程从fork返回后,接着执行下一条语句。
3. 一般来说,在fork之后是父进程先执行还是子进程先执行是不确定的,应用程序员无法控制。
1.1.2 fork使用实例分析
下面来分析一个使用fork的程序。forkbase.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <sys/types.h>
5
6 int glob = 6; /* external variable ininitialized data */
7 char buf[ ] = "a writeto stdout\n";
9 int main(void)
10 {
11 int var; /* automatic variable on the stack*/
12 pid_t pid;
13 var = 88;
14 if ((write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1))
15 { printf("writeerror\n"); exit(1); }
16 printf("before fork\n"); /* we don't flush stdout */
18 if ( (pid = fork()) < 0)
19 { printf("forkerror\n"); exit(1); }
20 else if (pid == 0) { /*child */
21 glob++; /* modifyvariables */
22 var++;
23 } else
24 sleep(2); /* parent */
25 printf("pid = %d, ppid = %d, glob = %d, var = %d\n",getpid(),getppid(), glob, var);
26 exit(0);
27 }
运行结果:
1 a write to stdout
2 before fork
3 pid = 2845, ppid = 2844,glob = 7, var = 89
4 pid = 2844, ppid = 2513, glob = 6, var = 88
运行结果分析:
结果的第1行是由父进程的14行打印。
结果的第2行是由父进程的16行打印。
由于父进程在24行睡眠了2秒,因此fork返回后,子进程先于父进程运行是大概率事件,所以子进程运行到25行打印出结果中的第3行。由于子进程会拷贝父进程的整个进程空间(这其中包括数据),因此当子进程18行从fork返回后,子进程中的glob=6,var=88(拷贝自父进程的数据)。此时子进程中pid=0,因此子进程会执行21、22行,当子进程到达25行时,将打印glob=7,var=89。
虽然,子进程改变了glob和var的值,但它仅仅是改变了子进程中的glob和var,而影响不了父进程中的glob和var。在子进程出生后,父、子进程的进程空间(代码、数据等)就是独立,互不干扰的。因此当父进程运行到25行,将会打印父进程中的glob和var的值,他们分别是6和88,这就是运行结果的第4行。
相关文章推荐
- 进程控制地字第1号系统调用——exec
- 进程控制——fork系统调用学习笔记
- Linux系统进程控制编程(二)——fork系统调用
- Head First C 第九章 进程与系统调用 用fork()+exec()运行子进程
- Linux学习记录--进程控制相关系统调用 推荐
- Linux系统编程_8_进程控制之fork_wait_waitpid函数
- 避免linux系统调用fork后产生僵死进程
- linux系统编程:进程控制(fork)
- linux系统下 fork()系统调用: 关于父子进程缓存问题的小坑
- 进程系统调用——fork函数深入理解
- 九、Linux系统编程-进程(二)fork系统调用、复制进程映像、写时复制、孤儿进程和僵尸进程
- 第三章 进程控制 3.1.3 exec系统调用3
- Linux常见文件系统操作命令、进程调用命令、进程控制C函数、时间函数
- 第三章 进程控制 3.1.3 exec系统调用2
- 进程控制相关函数以及系统调用
- Linux系统进程控制编程(五)——popen函数调用
- Operating Systems: Three Easy Pieces(操作系统:三个简单方面)5穿插章节:进程API/5.1系统调用:fork()
- Linux系统进程控制编程---popen函数调用
- Linux系统进程控制编程(四)——system系统调用
- Linux 系统调用之 fork()——进程的创建