首页 > 代码库 > 系统调用fork笔记

系统调用fork笔记

fork函数的原型是这样的:

1 pid_t fork(void); 

它实际上是一个系统调用,被包装在unistd.h中
由fork创建的新进程称为子进程,创建子进程的进程叫做父进程.子进程拥有与父进程一模一样的数据,从fork()语句开始分化.
它的返回值类型pid_t是一个内容为int的宏,在sys/types.h中声明.子进程返回0,父进程中返回子进程的pid(可以在子进程中调用getpid()得到,它同样被包装在unistd.h中).出错返回-1.出错原因可能是当前进程数超过限定或内存不足以新建一个进程.
除了init进程外,每一个进程都有一个父进程.init进程没有父进程,可以说,所有进程都是init进程fork出来的.


下面这个程序演示了fork函数.

 1 #include <assert.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <stdio.h> 5   6 int main(void) 7 { 8         pid_t childpid = fork(); /* 从这一行开始,子进程开始 */ 9         if (childpid == -1) /* 返回值为-1 -- 出错 */  10                 printf("出错啦!\n");  11         else if (childpid == 0) /* 返回值为0 -- 该进程为子进程 */12                 printf("我是子进程!我的pid为%x\n", getpid());   13         else if (childpid > 1) /*  14                                 * 返回值为正数 -- 该进程为父进程15                                 * 如果pid为1, 则表明父进程为init进程16                                 */17                 printf("我是父进程!我儿子的pid为%x\n", childpid);18         else /* 异常 */19                 assert(0);20         return 0;21 }        
/tmp louis$ gcc fork1.c -o f/tmp louis$ ./f我是父进程!我儿子的pid为1bf0我是子进程!我的pid为1bf0/tmp louis$ ./f我是父进程!我儿子的pid为1bf9我是子进程!我的pid为1bf9/tmp louis$ ./f我是父进程!我儿子的pid为1bfb我是子进程!我的pid为1bfb/tmp louis$ ./f我是父进程!我儿子的pid为1bfd我是子进程!我的pid为1bfd/tmp louis$ 

 

再看一个例子:

 1 #include <assert.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <stdio.h> 5   6 int main(void) 7 { 8         printf("%x: Hello, world!我只会显示一次!\n", getpid()); 9         pid_t childpid = fork();10         if (childpid == -1)11                 printf("出错啦!\n");12         else if (childpid == 0)  13                 printf("%x: Hello, world!我们父子会各自输出一次\n", getpid());14         else if (childpid > 1)15                 printf("%x: Hello, world!我们父子会各自输出一次\n", getpid());16         else /* 异常 */17                 assert(0);18         printf("%x: Hello, world!这次我们父子都会输出这条信息\n", getpid());19         wait();20         return 0;21 } 
/tmp louis$ gcc fork2.c -o f2/tmp louis$ ./f21c3e: Hello, world!我只会显示一次!1c3e: Hello, world!我们父子会各自输出一次1c3e: Hello, world!这次我们父子都会输出这条信息1c3f: Hello, world!我们父子会各自输出一次1c3f: Hello, world!这次我们父子都会输出这条信息/tmp louis$ ./f21c40: Hello, world!我只会显示一次!1c40: Hello, world!我们父子会各自输出一次1c40: Hello, world!这次我们父子都会输出这条信息1c41: Hello, world!我们父子会各自输出一次1c41: Hello, world!这次我们父子都会输出这条信息/tmp louis$  

倒数第3行的wait()调用的意思是:如果该进程拥有子进程且子进程还在运行,那么等待子进程结束后再继续执行之后的语句.
如果不加上这个调用,父进程就有可能先于子进程结束.这意味着,该父进程fork出的子进程没有了父进程,成了"孤儿"进程.这时,init进程会立刻"领养"该"孤儿"进程,成为"孤儿"进程的父进程.然后,被领养的"孤儿"进程结束运行后,内存中还保留这相关信息.这时,它就会变成"僵尸"进程,占用着宝贵的资源.
 
再看最后一个例子吧.这个例子说明了父子进程之间的数据资源并非共享.

 1 #include <assert.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <stdio.h> 5   6 int main(void) 7 { 8         int i = 0; 9         pid_t childpid = fork();10         if (childpid == -1)11                 printf("出错啦!\n");12         else if (childpid == 0) {13                 i = 1;14                 printf("子进程%x: i = %d\n", getpid(), i);15         } else if (childpid > 1) {16                 i += 1;17                 printf("父进程%x: i = %d\n", getpid(), i);18         } else   19                 assert(0);20         printf("%x: i = %d\n", getpid(), i);21         wait();22         return 0;23 } 
/tmp louis$ gcc fork3.c -o f3/tmp louis$ ./f3父进程1c88: i = 11c88: i = 1子进程1c89: i = 11c89: i = 1/tmp louis$ ./f3父进程1c8a: i = 11c8a: i = 1子进程1c8b: i = 11c8b: i = 1/tmp louis$  

子进程和父进程是完全不同的两个进程,父子进程要分别占用不同的资源。
 
最后出一道练习题:下面的代码执行后会有什么效果呢?

#include <unistd.h>int main(void)  {        while (1)                fork();        return 0;} 

大家想不出来可千万别自己去尝试呀,呵呵.

<style></style>