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$ ./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("出错啦!\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 = 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; }
大家想不出来可千万别自己去尝试呀,呵呵.
系统调用fork笔记
时间: 2024-10-14 22:18:19