fork()函数通过系统调用创建一个新进程(子进程),系统为它分配资源,并把其父进程的当前的情况复制到子进程中,从整体上看相当于有两份数据(所以简单改动一个进程内的变量的数据完全不会影响到另一个进程)。
fork()函数调用一次,返回两次。看下面一段代码
1 2 |
pid_t = fpid; fpid = fork(); |
这里fork()函数只出现了一次,即只被调用了一次,但此时它已经创建了另一个进程,所以共两个进程。可以理解为,父进程调用了fork()函数,创建子进程以后,fork()返回了一个值给父进程,同时还返回了一个值给新创建的子进程。
fork()函数有三种返回值:
1)在父进程中,fork()返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
上面提到fork()函数只复制所谓“当前的情况”,可以理解为“此时已经执行过的语句所呈现的情况”,所以时间上在fork()以后才执行的语句会在两个进程中执行。若有这样的语句:
1 2 |
fpid1 = fork(); fpid2 = fork(); |
则执行完后系统将会有4个进程。具体的,一开始只有进程1,执行完第一个fork()以后进程1创建了进程2,fork()分别给他们返回了一个值。之后,对于进程1和进程2,都是从得到一个返回值以后继续运行的,也就是分别调用了一次fork()。这样进程1就创建了进程3,进程2创建了进程4(此处数字不表示先后创建顺序,见下一段)。面试题中可能会有这样的考点,给出的例子是最简单最基本的情形。
创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
如果要控制fork()以后,等子进程执行完并返回了,再执行父进程,可以调用wait()函数。讲wait()函数之前,先简单讲一个概念:僵尸进程。
在UNIX系统下,若一个子进程结束了,但它的父进程没有等待(调用wait() /waitpid())它,那它将会变成一个僵尸进程。僵尸进程的危害在于:若不及时释放,它会一直占用着它的进程号,而系统所能使用的进程号是有限的,所以大量的僵尸进程可能导致系统无法创建新进程。
讲回wait()函数。进程一旦调用了wait(),就立即阻塞自己,并由wait自动分析是否有某个当前进程的子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
exit()函数可用于终结一个进程。exit的返回值是返回给操作系统的,但如果是多进程,则是返回给父进程的。
写了一个简单的小程序:创建子进程,在子进程中计算斐波那契数列并把父进程挂起。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#include <stdio.h> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t pid; //process id int i, a, b, fib; int n; scanf("%d", &n); pid = fork(); if(pid < 0) //说明当前进程是父进程且创建子进程失败 { fprintf(stderr, "Fork Failed\n"); exit(-1); } else if(pid == 0) //说明当前进程是子进程 { if(n == 1) printf("0\n"); else if(n == 2) printf("0 1\n"); else if(n > 2) { a = 0; b = 1; printf("0 1 "); for(i = 3; i <= n; i++) { fib = a + b; printf("%d%c", fib, " \n"[i == n]); a = b; b = fib; } } } else //说明当前进程是父进程 { wait(NULL); exit(0); } return 0; } |
文中提到的相关概念只做了很粗浅的解释,深入学习的话请移步下面链接。
reference:
0 等你来赞