一、fork 入门知识
一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
fork出错可能有两种原因:
1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。
2)系统内存不足,这时errno的值被设置为ENOMEM。
每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。
另外,还需提及一点:fork与execve的区别,fork是创建两个一模一样的进程,但是具有父子关系;而execve是将自己的进程替换成别人的进程,execve之后只有一个进程。
通俗的讲:fork是分身,而execve是变身。
僵死进程和孤儿进程含义
僵死进程
fork之后,父进程没有调用wait或者waitpid函数,子进程就退出了,这个时候子进程就成了僵死进程。子进程在退出前操作系统希望父进程得到子进程的退出码,所以父进程一定要wait子进程,这个可以避免僵死进程。
僵死进程的危害:系统内核在子进程退出是就释放其内存,但是系统并没有回收其pid,而系统的pid是有有限的,当系统内的僵死进程超过限制,系统将会崩溃。
孤儿进程
父进程在调用wait或waitpid之前就已经退出了,也就是父进程早于子进程退出,此时init进程就成为了子进程的父进程,init进程为子进程的父进程收集退出状态,从而避免僵死进程。孤儿进程并没有危害,常运行于后台。
通俗的解释僵死进程就是:儿子死了,老子不知道;孤儿进程:老子死了,儿子还在。
下面以两道例题来巩固fork的使用
1、编写一个孤儿进程,这个孤儿进程可以同时创建100个僵死进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int