进程回收

回收子进程

孤儿进程

         孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

init进程:孤儿回收进程, 进程ID为1的进程,/sbin/init;有时分配的是对应user下的init进程 

测试孤儿进程的程序:

int main(void)
{
	printf("测试孤儿进程的父进程\n");
	pid_t pid = fork();
	if(pid < 0)
	{	
		perror("fork error");
		exit(-1);
	}else if(pid > 0){
		printf("this is parent process, id is %d\n", getpid());
		sleep(3);
		printf("parent died!!!\n");
	}else{
		printf("this is child process pid = %d,  ppid = %d\n", getpid(), getppid());
		sleep(5);
		printf("this is child process pid = %d,  ppid = %d\n", getpid(), getppid());
	}
	
	return 0;
}

                                      

僵尸进程(Z):子进程的PCB仍然留在内核中

僵尸进程: 进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。 

PCB存在的一个意义:方便父进程根据PCB回收子进程的资源

Linux下子进程死亡,父进程未回收,子进程在ps aux打印出的进程信息中,显示 [子进程]<defunct> 代表死亡

Linux进程异常终止,都是接受了某一个信号导致的。查看Linux下所有的信号类型,使用kill -l

测试僵尸进程的程序:

int main(void)
{
	printf("测试僵尸进程\n");
	pid_t pid = fork();
	if(pid < 0)
	{	
		perror("fork error");
		exit(-1);
	}else if(pid > 0){
		while(1)
		{
			printf("this is parent process, id is %d\n", getpid());
			sleep(3);
		}
	}else{
		printf("this is child process pid = %d,  ppid = %d\n", getpid(), getppid());
		sleep(5);
		printf("child process dead!!!\n");
	}
	
	return 0;
}

僵尸进程在终端中的显示:

 

特别注意,僵尸进程是不能使用kill命令清除掉的。因为kill命令只是用来终止进程的,而僵尸进程已经终止。思考!用什么办法可清除掉僵尸进程呢?

此时,使用ps命令无法杀死僵尸进程,为了解决此类问题,可以杀死该僵尸进程原来的父进程,此时该僵尸进程成为孤儿进程,对应的父进程成为init,init看到它是僵尸进程就将其回收!!!

wait函数:回收子进程的资源,一次wait只可以回收一个子进程

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中用特殊变量$?查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:

1 父进程阻塞等待子进程退出,此时父进程不会做其他事

2 回收子进程残留资源,即残留在内存中的PCB

3 获取子进程结束状态(退出原因)。通过status获取

    pid_t wait(int *status); 成功:清理掉的子进程ID;失败:-1 (没有子进程),int *status是传出参数

当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)

可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:

 *1.  WIFEXITED(status) 为非0  → 进程正常结束

         WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)

 *2.  WIFSIGNALED(status) 为非0 → 进程异常终止

         WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。

3. WIFSTOPPED(status) 为非0 → 进程处于暂停状态

         WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。

         WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行

wait函数的简单使用:

1、wait粗略回收单个进程,不返回进程终止状态

int main(void)
{
	pid_t wpid;
	printf("防止僵尸进程产生,wait等待回收子进程的资源\n");
	pid_t pid = fork();
	if(pid < 0)
	{	
		perror("fork error");
		exit(-1);
	}else if(pid > 0){
		wpid = wait(NULL);  //粗略回收
		if(wpid == -1)   //父进程代码执行前,先等待回收子进程的资源
		{
			perror("wait error");
			exit(-1);
		}
		while(1)
		{
			printf("this is parent process, id is %d\n", getpid());
			sleep(2);
		}
	}else{
		printf("this is child process pid = %d,  ppid = %d\n", getpid(), getppid());
		sleep(5);
		printf("child process dead!!!\n");
	}
	
	return 0;
}

2、wait回收精确子进程,判断进程状态返回值

//精确回收,手动用kill杀子进程看返回值
int main(void)
{
	pid_t wpid;
	int status;
	printf("防止僵尸进程产生,wait等待回收子进程的资源\n");
	pid_t pid = fork();
	if(pid < 0)
	{	
		perror("fork error");
		exit(-1);
	}else if(pid > 0){
		wpid = wait(&status);  //精确回收
		if(wpid == -1)   //父进程代码执行前,先等待回收子进程的资源
		{
			perror("wait error");
			exit(-1);
		}
		if(WIFEXITED(status))
		{	
			printf("child exit with %d\n", WEXITSTATUS(status));
		}
		if(WIFSIGNALED(status))
		{
			printf("child killd by %d\n", WTERMSIG(status));
		}
		while(1)
		{
			printf("this is parent process, id is %d\n", getpid());
			sleep(3);
		}
	}else{
		printf("this is child process pid = %d,  ppid = %d\n", getpid(), getppid());
		sleep(60);
		return 100;
	}
	
	return 0;
}

waitpid函数:相较于wait函数更灵活

作用同wait,但可指定pid进程清理,可以不阻塞。

    pid_t waitpid(pid_t pid, int *status, int options); 成功:返回清理掉的子进程ID;失败:-1(无子进程)

特殊参数和返回情况:options:指定可以不阻塞回收

参数pid:

> 0 回收指定ID的子进程  

-1 回收任意子进程(相当于wait)

0 回收和当前调用waitpid一个组的所有子进程

< -1 回收指定进程组内的任意子进程

函数特殊返回:返回0:参3设置为WNOHANG,且子进程正在执行时,返回0

参3为WNOHANG,不阻塞回收,且子进程正在运行。每次会探测子进程是否结束,未结束就返回,所以为了让其能确定的回收子进程,必须轮询检测

参数3为0时,此时等价于wait() 函数

int n = 5;
do{
    pid = waitpid(-1, NULL, WNOHANG);  //pid == 0表示子进程仍在运行
    if(pid > 0)
    {
        n--;
    }

}while(n == 0);

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。                                                杀死进程组:kill -9 -进程组ID

注:循环回收多个子进程的方法:

//回收多个子进程
while(wait(NULL));  
//or  
while(waitpid(-1, NULL, 0));
//or
int n = 5;
do{
	pid = waitpid(-1, NULL, WNOHANG);  //pid == 0表示子进程仍在运行
	if(pid > 0)
	{
		n--;
	}
}while(n == 0);

 

好的,这是一个具体的问题,我可以给你提供一个基于C语言的实现思路,你只需要按照这个思路来编写代码即可。 1. 父进程创建子进程,使用fork函数实现。 2. 子进程安装信号处理函数,使用signal函数设置SIGINT和SIGQUIT信号的处理函数。 3. 子进程进入循环,等待接收信号。在信号处理函数中,统计收到的信号次数,并打印输出信息。 4. 父进程使用sleep函数定时向子进程发送信号。在父进程中,使用kill函数向子进程发送信号。 5. 子进程收到20个SIGINT信号和10个SIGQUIT信号后,退出进程。 6. 父进程使用wait函数回收进程。 下面是一个简单的示例代码,你可以参考一下: ``` #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> volatile int sigint_count = 0; volatile int sigquit_count = 0; void sigint_handler(int signum) { sigint_count++; printf("我是子进程,我收到父进程发送的SIGINT信号第%d次\n", sigint_count); } void sigquit_handler(int signum) { sigquit_count++; printf("我是子进程,我收到父进程发送的SIGQUIT信号第%d次\n", sigquit_count); } int main() { pid_t pid = fork(); if(pid == 0) { signal(SIGINT, sigint_handler); signal(SIGQUIT, sigquit_handler); while(sigint_count < 20 || sigquit_count < 10) { sleep(1); } exit(0); } else if(pid > 0) { int i; for(i = 0; i < 20; i++) { sleep(1); kill(pid, SIGINT); } for(i = 0; i < 10; i++) { sleep(2); kill(pid, SIGQUIT); } wait(NULL); } else { perror("fork"); exit(1); } return 0; } ``` 希望这个思路能对你有所帮助,如果你有其他问题,欢迎继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Star星屹程序设计

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值