首先我们要知道:
在类UNIX操作系统中,子进程是通过父进程创建的,子进程再创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。
在类UNIX操作系统中,子进程是通过父进程创建的,子进程再创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。
概念:
孤儿进程:
当一个父进程由于正常完成工作而退出或由于其他情况被终止,它的一个或多个子进程却还在运行,那么那些子进程将成为孤儿进程。
我们可以这样理解:一个父亲他走的很安详,但是他的众多子孙后代还没走,于是乎他们就成了孤儿。
But ...
但是我们的操作系统为避免孤儿进程退出时无法释放所占用的资源而僵死,进程号为1的init进程将会接受并处理这些孤儿进程,这一过程也被称为“收养” re-parenting
当一个父进程由于正常完成工作而退出或由于其他情况被终止,它的一个或多个子进程却还在运行,那么那些子进程将成为孤儿进程。
我们可以这样理解:一个父亲他走的很安详,但是他的众多子孙后代还没走,于是乎他们就成了孤儿。
But ...
但是我们的操作系统为避免孤儿进程退出时无法释放所占用的资源而僵死,进程号为1的init进程将会接受并处理这些孤儿进程,这一过程也被称为“收养” re-parenting
实现孤儿进程:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<error.h>
#include<stdlib.h>
int main()
{
pid_t pd=fork();
if(pd>0){
printf("I am a father pid=%d\n",getppid());
sleep(5);
exit(0);
}
else if(pd==0){
printf("I am a child pid=%d\n",getpid());
sleep(20);
}else{
perror("fork error");
return 1;
}
}
僵尸进程:
子进程在退出之后,他的父进程没有去等待( wait()或waitpid() )它,然后它就成了僵尸进程 Zombie / defunct
我们也可以这样理解:一个孩子的魂没有了,但是他的父亲并没有去安葬它,久而久之,它就成了一个僵尸(惊悚不)。
但是呢 . . .
UNIX提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。
子进程在退出之后,他的父进程没有去等待( wait()或waitpid() )它,然后它就成了僵尸进程 Zombie / defunct
我们也可以这样理解:一个孩子的魂没有了,但是他的父亲并没有去安葬它,久而久之,它就成了一个僵尸(惊悚不)。
但是呢 . . .
UNIX提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。
这种机制就是:在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号,退出状态,运行时间等),所以只要我们父进程找到子进程,使用wait()或waitpid()去解决这个问题,这些
通过task_struct(PCB)保存的信息就能被释放掉。
实现僵尸进程
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<error.h>
int main(int argc,char*argv[])
{
pid_t fd=fork(); //创建子进程,父进程返回子进程ID,子进程返回0,失败返回-1
if(fd==0){
printf("child:pid is %d ppid is %d\n",getpid(),getppid());
sleep(2);
exit(1);
}else if(fd>0){
while(1){
printf("parent: pid is %d ppid is %d\n",getpid(),getppid());
sleep(1);
}
}else{
printf("fork error");
}
}
危害:
孤儿:每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。 因此孤儿进程并不会有什么危害
孤儿:每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。 因此孤儿进程并不会有什么危害
僵尸:上面提到的task_struct(PCB)保存的信息,万一父进程一直没有去调用wait()或waitpid()解决问题的话,那么我们的PCB就会一直去维护这些信息,同时进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。 很伤...
如何让去避免僵尸进程
1.调用signal(SIGCHLD,SIG_IGN)函数
SIGCHLD:一个信号。当子进程快要结束,会向父进程发送一个SIGCHLD信号,告诉父进程我快结束,赶快调用wait函数,来回收子进程的退 出状态和其他信息。
SIG_ING:忽略的意思,将此信号的处理方式设置为忽略,可让内核把僵尸进程转交给init进程去处理,省去了大量僵尸进程占用系统资源
代码块:
void signal_wait(int sig)
{
wait(NULL);
printf("child is cleaned\n");
}
int main(void)
{
pid_t pid=fork();
signal(SIGCHLD,signal_wait)
if(pid<0)
{
perror("fork error ");
}else if(pid>0)
{//父进程
sleep(1);
printf("parent: pid is %d ppid is %d\n",getpid(),getppid());
}else{//pid=0//子进程
printf("child: pid is %d ppid is %d\n",getpid(),getppid());
}
return 0;
}
2.两次fork(),再调用waitpid()函数
why.?
因为呢,在fork()一次之后,得到父进程a和子进程b,然后在子进程里面再fork(),得到孙进程c 然后进程b退出,这时候的孙进程c由于失去了父进程,就会被inti进程收养,然后进行处理。而我们的b进程就会被waipid()捕获,然后回收掉。
why.?
因为呢,在fork()一次之后,得到父进程a和子进程b,然后在子进程里面再fork(),得到孙进程c 然后进程b退出,这时候的孙进程c由于失去了父进程,就会被inti进程收养,然后进行处理。而我们的b进程就会被waipid()捕获,然后回收掉。
waitpid(pid_t pid,int *status,int options) 了解一下 ...
参数:
pid:
pid>0 只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结 束,waitpid就会一直等 下去。
pid=-1 等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
参数:
pid:
pid>0 只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结 束,waitpid就会一直等 下去。
pid=-1 等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
status:状态,一般置为NULL
options:目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用"|"运算符把它们连接起来使用
例如:waitpid(-1,NULL,WNOHANG | WUNTRACED) 若不想使用可以置0
返回值:
当正常返回的时候,waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
例如:waitpid(-1,NULL,WNOHANG | WUNTRACED) 若不想使用可以置0
返回值:
当正常返回的时候,waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
代码块:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<error.h>
int main(int argc,char*argv[])
{
pid_t fd=fork(); //创建子进程,父进程返回子进程ID,子进程返回0,失败返回-1
if(fd==0){
printf("child:pid is %d ppid is %d\n",getpid(),getppid());
pid_t fd=fork(); //子进程的fork
sleep(2);
exit(1);
}else if(fd>0){
waitpid(fd, NULL, 0); //等待退出的子进程,父进程仅仅给子进程收尸,并不需要子进程的返回值
while(1){
printf("parent: pid is %d ppid is %d\n",getpid(),getppid());
sleep(1);
}
}else{
printf("fork error");
}
}
3.将僵尸进程变成孤儿进程,然后他就会被inti进程回收掉。
因为僵尸进程的父进程肯定存在,找到这个父进程,kill掉它。
因为僵尸进程的父进程肯定存在,找到这个父进程,kill掉它。