Linux 回收子进程 wait/waitpid函数

Linux 回收子进程 wait/waitpid函数

前提

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

描述

wait for process to change state;
等待进程改变状态;
A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a signal. In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is not performed, then the terminated child remains in a “zombie” state;
等待子进程终止,回收任意已终止的子进程,释放其占用的资源;
如果有子进程存在且未终止,wait使父进程处于挂起状态;
waitpid的第三个参数控制是否等待,如果不等待,子进程终止后会处于僵尸状态;

wait

  1. 阻塞等待子进程退出;
  2. 回收子进程残留资源;
  3. 获取子进程结束状态(退出原因);

头文件
#include <sys/types.h>
#include <sys/wait.h>
函数签名
pid_t wait(int *status);
参数

status:
int *类型的传出参数,表示被回收子进程的终止原因(正常终止(最后的返回值)或被信号杀死(被什么信号杀死));
传NULL表示,不关系终止原因;

返回值

成功:
返回被回收进程的id;
失败:
返回-1;
并设置errno;

示例

父进程运行到wait,等待10秒,回收子进程(多个子进程,父进程运行几个wait随机回收几个子进程),结束;

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
	pid_t pid, wpid;
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}
	if(!pid){
		sleep(10);
		exit(1);
	}
	wpid = wait(NULL);
	if(wpid == -1){
		perror("wait");
		exit(1);
	}
	printf("wait for %d\n", wpid);
	return 0;
}

status(终止原因)

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

WIFEXITED(正常终止)

WIFEXITED(status)
正常终止,返回true;
WEXITSTATUS(status)
如果正常终止,通过该宏获取进程终止时的状态;

示例
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
	pid_t pid, wpid;
	int status;
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}
	if(!pid){
		sleep(10);
		return 20;
	}
	wpid = wait(&status);
	if(wpid == -1){
		perror("wait");
		exit(1);
	}
	printf("wait for %d\n", wpid);
	if(WIFEXITED(status)){
		printf("return val is %d\n", WEXITSTATUS(status));
	}
	return 0;
}
WIFSIGNALED(信号杀死)

WIFSIGNALED(status)
进程非正常终止,返回true;
WTERMSIG(status)
当WIFSIGNALED(status)为true时,获取终止进程的信号的编号;

示例
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
	pid_t pid, wpid;
	int status;
	pid = fork();
	if(pid == -1){
		perror("fork");
		exit(1);
	}
	if(!pid){
		sleep(30);
		exit(1);
	}
	wpid = wait(&status);
	if(wpid == -1){
		perror("wait");
		exit(1);
	}
	printf("wait for %d\n", wpid);
	if(WIFSIGNALED(status)){
		printf("the child process terminated by %d\n", WTERMSIG(status));
	}
	return 0;
}
kill signal

用 kill终止对应的进程,默认 15号信号;

kill [options] <pid> [...]
kill -9 1644 // 用 9号信号终止 1644进程

查看信号

kill -l

非实时信号: 34号往前(一般用的);
实时信号: 34号往后;
在这里插入图片描述

WIFSTOPPED(进程暂停)

waitpid


函数签名
pid_t waitpid(pid_t pid, int *status, int options);
参数

pid

pidmeaning
< -1绝对值表示,对应进程组id,回收该组 id中,调用进程(父进程)的任意子进程
== -1回收任意子进程,相当于wait
== 0回收和调用进程(父进程)同一进程组的任意子进程
> 0回收指定pid的子进程

status
终止原因;
options

optionsmeaning
0表示挂起方式回收,只要有子进程,至少等待一个子进程结束,回收子进程,返回
WNOHANG非挂起方式,如果没有子进程已经结束了,立即返回
返回值

成功:
返回被回收进程的id;
如果子进程存在,且均没有结束,并且 options为 WNOHANG,返回 0;
失败:
返回-1,并设置errno;

示例
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){
	pid_t pid, wpid, rpid;
	int status;
	for(int i=0; i<4; i++){
		pid = fork();
		if(pid == -1){
			perror("fork");
			exit(1);
		}
		if(i == 2 && pid){
			wpid = pid;
		}
		if(!pid){
			sleep(10);
			return 10;
		}
	}
	rpid = waitpid(wpid, &status, 0); // 挂起
	// rpid = waitpid(wpid, &status, WNOHANG); // 非挂起
	if(rpid== -1){
		perror("wait");
		exit(1);
	}
	if(!rpid){
		printf("No hang on and there is no exited child process.\n");
		exit(1);
	}
	printf("wait for %d\n", wpid);
	if(WIFEXITED(status)){
		printf("return val is %d\n", WEXITSTATUS(status));
	}
	if(WIFSIGNALED(status)){
		printf("the child process terminated by %d\n", WTERMSIG(status));
	}
	return 0;
}

学习笔记

  1. 一次 wait 或 waitpid 调用只能清理一个子进程,清理多个子进程应使用循环;
  2. 当进程终止时,操作系统的隐式回收机制会:
    a. 关闭所有文件描述符 ;
    b. 释放用户空间 分配的内存。内核的 PCB 仍存在。其中保存该进程的退出状态。

2020/07/25 13:59
@luxurylu

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值