Linux多进程编程之 孤儿进程&僵尸进程+wait函数

我们可否想过一个问题:使用fork()函数创建子进程,因为父进程和子进程的执行顺序是随机的

当父进程已经结束了,子进程还会继续存在并正常执行吗?

我们先看这个例子:

guer1.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
 
int main()
{
	pid_t pid = fork(); 
	if(pid<0)
		perror("fork error!");    
	else if (pid>0)
		printf("I am parent , pid is %d\n",getpid());
	else{
		sleep(2);//保证父进程优先结束
		printf("I am child , pid is %d\n",getpid());
	}  
    
	return 0;
} 

我们控制父进程优先执行,可以看到子进程还是可以执行,但是为什么把输出越界了呢,而不是正常的在两个指令行之间输出?

因为父进程结束的时候我们程序就退出了,而两个指令行之间的空间是我们上面执行的程序的空间,子进程执行的位置发生了变化,说明他已经不在属于刚才的父进程了,那是怎么肥四呢?

其实当父进程执行完毕之后,就结束了,子进程的爹没了,他就变成了一个孤儿进程,那他怎么办呢?他找到了他的 太太太太太爷爷,init进程( 在Linux系统中,当内核启动了自己之后,init是第一个用户进程,它的进程号是1,是系统中所有其他用户进程的祖先进程 ),这时候init进程会带领被父亲抛弃的孤儿进程执行完毕后退出。

我们可以看一下下面这个程序对这个问题的实践

guer2.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
	pid_t pid=fork();
	if(pid<0)
		perror("fork error!");
	else if(pid==0){//child 
		printf("I am child process \n");
		printf("pid:%d----ppid:%d\n",getpid(),getppid());
		printf("I will sleep for 3 seconds\n");
		sleep(3);
		printf("pid:%d----ppid:%d\n",getpid(),getppid());
		printf("child process exit\n");
	}
	else{//parent 
		printf("I am father process\n");
		sleep(1);//让子进程执行到sleep
		printf("father process exit\n");	
	}

	return 0;
}

我们可以看到父进程结束后,子进程的ppid变成了1,成功地被init进程收养了

刚才说的是父进程优先执行完成,那么子进程优先完成的时候怎么办呢?也是直接退出吗?

答案是不是的

正常情况下,子进程是通过父进程创建的,子进程的结束和父进程的运行是一个异步过程,即父进程无法预测子进程 到底什么时候结束。

unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息,就可以得到。

这种机制就是: 在每个进程退出的时候,内核会向父进程发送SIGCHLD信号,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号,退出状态,运行时间等),保留的这些信息就是所谓的僵尸进程!

我们可以看一下僵尸的状态:

jiangshi.c

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
	pid_t pid=fork();
	if(pid<0)
		perror("fork error!");
	else if(pid==0){
		printf("I am child process,I exit\n");
		exit(0);
	}
	printf("I am father process\n");
	printf("I will sleep for 2 seconds\n");
	sleep(2);//等待子进程退出
	system("ps -o pid,ppid,state,tty,command");//输出进程信息 
	printf("father process exit\n");
	return 0;
}

Z的意思就是 Zombie,即僵尸

根据僵尸进程的定义我们可以知道,只要是创建了子进程,一定会产生僵尸进程,所以僵尸进程并不可怕,但是僵尸进程是占内存的,我们知道一般进程只能同时存在300-400个左右,假如我们写了一个非常大的程序,需要不断的创建非常多的子进程,正常情况下我们渴望的是让他们轮流存活,也就是交替的创建和销毁,但是如果我们没能及时的销毁这些僵尸进程导致累积过多的话,就会使得后面的进程创建失败而使得程序作废

那么僵尸进程是怎么被处理的呢?Linux提供了一个wait()函数,wait很明显是等待的意思,但是他和sleep()不一样,不是挂起不执行,而是等待子进程一起结束,他可以使父进程获得子进程的信息,在父进程执行完成退出之前销毁子进程产生的僵尸进程,关于wait()的机制可以看下面的这两个图,就不再详细讲了。。

其实是两个函数wait()和waitpid(),二者的作用比较类似,都是处理僵尸进程,但是调用的方法及作用也有区别,我们来看一下:

pid_t wait(int* status)

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是   否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回,返回值是销毁的子进程的id;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。  参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,我们就可以设定这个参数为NULL ,想知道的话是可以百度的到的,嘻嘻

pid = wait(NULL); 

wait1.c

#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	pid_t pc,pr;
	pc=fork();
	if(pc<0)
		perror("creat process error");
	else if(pc==0){
		printf("child process,pid is %d\n",getpid());
		sleep(10);
	}
	else {
		pr=wait(NULL);
		printf("parent proccess,my child pid is %d\n",pr);
	}
	exit(0);
}

这样父进程就可以获得子进程的pid

pid_t waitpid(pid_t pid , int* status , int options)

返回值和参数status和wait()函数一样,

pid:

pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。

pid==-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。

pid==0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。

pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

options:

options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,如果使用了WNOHANG(wait no hung)参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到, 如果我们不想使用它们,也可以把options设为0

pid=waitpid(-1,NULL,0);

waitpid1.c

#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
	pid_t pc,pr;
	pc=fork();
	if(pc<0)
		perror("creat process error");
	else if(pc==0){
		printf("child process,pid is %d\n",getpid());
		sleep(10);
	}
	else {
		pr=waitpid(-1,NULL,0);
		printf("parent proccess,my child pid is %d\n",pr);
	}
	exit(0);
}

我们使得waitpid和wait函数取得了相同的效果,赞!

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值