Linux 进程回收的系统调用(wait、waitpid)

Linux 进程回收的系统调用(wait、waitpid)

  在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块PCB的信息(包括进程号、退出状态、运行时间等)。
  父进程可以通过调用 waitwaitpid 得到它的退出状态同时彻底清除掉这个进程

注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。

wait 系统调用

  1. 函数接口
#include <sys/types.h>
#include <sys/wait.h>

pid_t wait(int *status);
  1. 函数功能:等待任意一个子进程结束,如果任意一个子进程结束,则会回收子进程的PCB块

  2. 参数:

  • status :进程退出时的状态信息,传入的是int* ,传出参数
  1. 返回值
  • 调用成功,返回被回收的子进程ID
  • 调用失败,返回-1(所以子进程结束 / 调用函数出错)

  调用 wait 函数,则进程会被挂起(阻塞),直到一个子进程退出,或者收到一个不能被忽略的信号,进程才会被唤醒。如果没有子进程,函数立刻返回 -1 。如果子进程都结束,函数仍立刻返回 -1

示例1:僵尸进程

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>


int main(){
	pid_t pid;
	
	// 创建 5 个子进程
	for (int i = 0; i < 5; i++){
		pid = fork();
		if (pid == 0){	// 子进程中不再创建新的子进程
			break;
		}
	}
	
	if (pid > 0){		// 父进程
		while(1){
			printf("I am parent process: pid = %d\n", getpid());
			sleep(1);
		}
	}
	else if (pid == 0){	// 子进程
		printf("child: pid = %d\n", getpid());
	}
	
	return 0;
}

编译执行,子进程在执行输出语句后结束,而父进程则仍在执行。
wait
查看进程状态,可以看到5个子进程处于僵尸状态
僵尸进程

示例2:wait 阻塞


#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>


int main(){
	pid_t pid;
	
	// 创建 5 个子进程
	for (int i = 0; i < 5; i++){
		pid = fork();
		if (pid == 0){	// 子进程中不再创建新的子进程
			break;
		}
	}
	
	if (pid > 0){		// 父进程
		while(1){
			printf("I am parent process: pid = %d\n", getpid());
			
            int ret = wait(NULL);		// wait 阻塞,等待子进程退出
            if (ret == -1){				// 所有子进程退出,返回 -1,则退出循环
				break;
			}
            printf("child die, pid = %d\n", ret);
            
            sleep(1);
		}
	}
	else if (pid == 0){	// 子进程
        while(1){		// 子进程一直执行
            printf("child: pid = %d\n", getpid());
            sleep(1);
        }
		
	}
	
	return 0;
}

  编译执行,父进程在 wait 处阻塞,等待子进程结束,回收子进程。如果在另一个终端,kill 某一个子进程如kill -9 83215,则wait 函数会回收该子进程,输出 child die, pid = 83215
阻塞

示例3:返回进程状态

含义
WIFEXITED(status)非0,进程正常退出
WEXITSTATUS(status)如果上宏为真,获取进程退出的状态(exit的参数)
WIFSIGNALED(status)非0,进程异常终止
WTERMSIG(status)如果上宏为真,获取使进程终止的信号编号
WIFSTOPPED(status)非0,进程处于暂停状态
WSTOPSIG(status)如果上宏为真,获取使进程暂停的信号的编号
WIFCONTINUED(status)非0,进程暂停后已经继续运行
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>


int main(){
	pid_t pid;
	
	// 创建 5 个子进程
	for (int i = 0; i < 5; i++){
		pid = fork();
		if (pid == 0){	// 子进程中不再创建新的子进程
			break;
		}
	}
	
	if (pid > 0){		// 父进程
		while(1){
			printf("I am parent process: pid = %d\n", getpid());
			
            int st;

            int ret = wait(&st);
            if (ret == -1){				// 所有子进程退出,返回 -1,则退出循环
				break;
			}

            // 是否正常退出
            if (WIFEXITED(st)){
                printf("退出状态码:%d\n", WEXITSTATUS(st));
            }
            // 是否为信号中断
            if (WIFSIGNALED(st)){
                printf("信号中断: %d\n", WTERMSIG(st));
            }


            printf("child die, pid = %d\n", ret);
            
            sleep(1);
		}
	}
	else if (pid == 0){	// 子进程
        printf("child: pid = %d\n", getpid());
        sleep(1);
		
	}
	
	return 0;
}

  5个子进程在执行输出后结束,wait 依次回收子进程资源,在所有子进程退出后,父进程的wait 返回 -1 ,退出循环,结束父进程。
wait

waitpid 系统调用

  1. 函数原型
#include <sys/types.h>
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);
  1. 函数功能:waitpid 回收指定进程号的子进程。可以指定是否阻塞。

  2. 参数

  • pid > 0:某个子进程的pid
  • pid = 0:回收当前进程组的所有子进程
  • pid = -1:回收所有子进程,相当于调用 wait()
  • pid < -1:等待任意进程组的组ID的绝对值,回收指定进程组的子进程。
  • options:设置阻塞(0) / 非阻塞(WNOHANG
  1. 返回值
  • >0:返回子进程id
  • =0:option=WNOHANG 表示还有子进程没有退出
  • -1 :表示错误 / 没有子进程

示例4:wait(&st) 等价于 waitpid(-1, &st, 0)-1 代表回收所有子进程,0 表示阻塞等待



编译执行,与示例3结果相同,父进程阻塞等待,在子进程执行结束,回收子进程。

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>


int main(){
	pid_t pid;
	
	// 创建 5 个子进程
	for (int i = 0; i < 5; i++){
		pid = fork();
		if (pid == 0){	// 子进程中不再创建新的子进程
			break;
		}
	}
	
	if (pid > 0){		// 父进程
		while(1){
			printf("I am parent process: pid = %d\n", getpid());
			
            int st;

            int ret = waitpid(-1, &st, 0);		// 等价于 wait(&st);
            if (ret == -1){				// 所有子进程退出,返回 -1,则退出循环
				break;
			}
			
            // 是否正常退出
            if (WIFEXITED(st)){
                printf("退出状态码:%d\n", WEXITSTATUS(st));
            }
            if (WIFSIGNALED(st)){
                printf("信号中断: %d\n", WTERMSIG(st));
            }


            printf("child die, pid = %d\n", ret);
            
            sleep(1);
		}
	}
	else if (pid == 0){	// 子进程
        printf("child: pid = %d\n", getpid());
        sleep(1);
	}
	
	return 0;
}

waitpid

示例5:非阻塞waitpid

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>


int main(){
	pid_t pid;
	
	// 创建 5 个子进程
	for (int i = 0; i < 5; i++){
		pid = fork();
		if (pid == 0){	// 子进程中不再创建新的子进程
			break;
		}
	}
	
	if (pid > 0){		// 父进程
		while(1){
			printf("I am parent process: pid = %d\n", getpid());
			sleep(1);

            int st;
            int ret = waitpid(-1, &st, WNOHANG);
            if (ret == -1){				// 所有子进程退出,返回 -1,则退出循环
				break;
			}   
            else if (ret == 0){      // 还有其他子进程在执行
                continue;       // 父进程进行下一个判断
            }
            else if (ret > 0){		// 某个子进程退出,输出状态信息
                // 是否正常退出
                if (WIFEXITED(st)){
                    printf("退出状态码:%d\n", WEXITSTATUS(st));
                }

                if (WIFSIGNALED(st)){
                    printf("信号中断: %d\n", WTERMSIG(st));
                }


                printf("child die, pid = %d\n", ret);
            }
            
		}
	}
	else if (pid == 0){	// 子进程
        while(1){
            printf("child: pid = %d\n", getpid());
            sleep(1);
        }
        exit(0);
	}
	
	return 0;
}

在另一个终端 kill -9 子进程号 ,则父进程会输出中断信号
kill
在这里插入图片描述
  今天介绍了两个进程回收的两个系统调用 waitwaitpid ,其区别是wait 是阻塞模式等待,waitpid 可以设置阻塞 或者 非阻塞,还可以设置回收哪个/哪些进程。

一键三连是对我的支持与鼓励!欢迎关注编程小镇,每天学一点新姿势😄。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值