fork函数笔记

2014/7/11 20:54编辑: 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("出错啦!
");  
11         else if (childpid == 0) /* 返回值为0 -- 该进程为子进程 */
12                 printf("我是子进程!我的pid为%x
", getpid());   
13         else if (childpid > 1) /*  
14                                 * 返回值为正数 -- 该进程为父进程
15                                 * 如果pid为1, 则表明父进程为init进程
16                                 */
17                 printf("我是父进程!我儿子的pid为%x
", 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!我只会显示一次!
", getpid());
 9         pid_t childpid = fork();
10         if (childpid == -1)
11                 printf("出错啦!
");
12         else if (childpid == 0)  
13                 printf("%x: Hello, world!我们父子会各自输出一次
", getpid());
14         else if (childpid > 1)
15                 printf("%x: Hello, world!我们父子会各自输出一次
", getpid());
16         else /* 异常 */
17                 assert(0);
18         printf("%x: Hello, world!这次我们父子都会输出这条信息
", getpid());
19         wait();
20         return 0;
21 } 
/tmp louis$ gcc fork2.c -o f2
/tmp louis$ ./f2
1c3e: Hello, world!我只会显示一次!
1c3e: Hello, world!我们父子会各自输出一次
1c3e: Hello, world!这次我们父子都会输出这条信息
1c3f: Hello, world!我们父子会各自输出一次
1c3f: Hello, world!这次我们父子都会输出这条信息
/tmp louis$ ./f2
1c40: 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("出错啦!
");
12         else if (childpid == 0) {
13                 i = 1;
14                 printf("子进程%x: i = %d
", getpid(), i);
15         } else if (childpid > 1) {
16                 i += 1;
17                 printf("父进程%x: i = %d
", getpid(), i);
18         } else   
19                 assert(0);
20         printf("%x: i = %d
", getpid(), i);
21         wait();
22         return 0;
23 } 
/tmp louis$ gcc fork3.c -o f3
/tmp louis$ ./f3
父进程1c88: i = 1
1c88: i = 1
子进程1c89: i = 1
1c89: i = 1
/tmp louis$ ./f3
父进程1c8a: i = 1
1c8a: i = 1
子进程1c8b: i = 1
1c8b: i = 1
/tmp louis$  

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

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

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

原文地址:https://www.cnblogs.com/KM-Y/p/3790623.html