Linux系统编程---进程等待

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shanghx_123/article/details/83549815

进程等待

1.为什么需要进程等待

  1. 我们知道,子进程退出,父进程如果不管不顾,那么子进程就可能变为僵尸进程,进而占用系统资源,造成内存泄漏,
  2. 另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill-9也无能为力,因为谁也办法杀死一个已经死去的进程。
  3. 有时候我们需要知道父进程派给子进程的任务完成的如何如,子进程运行完成,结果对还对,或者是否正常退出。在这些情况,我们需要等待子进程执行完。
  4. 所以要想知道子进程的状态,就需要父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。
    总结就是:防止产生僵尸进程,从而导致系统资源占用和浪费,影响性能。

2.进程等待的方法

当一个进程正常或异常终止时,内核就向其父进程发送 SIGCHLD 信号,相当于告诉父亲他哪个儿子挂了,而父进程可以通过 wait() 或 waitpid() 函数等待子进程结束,获取子进程结束时的状态,同时回收他们的资源(相当于,父亲听听死去儿子的遗言同时好好安葬它)。

wait

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

pid_t wait(int *status);

返回值: 成功返回被等待进程pid,失败返回-1。
参数status: 输出型参数,获取子进程退出状态,不关心子进程的状态则可以设置为NULL。

当参数status的值不是NULL,即当要获取子进程的退出状态时,需要用宏来取它的退出信息。由于wait和waitpid的status是一样的,所以在下面会详细介绍

功能: 等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。
注意:

  1. 在每个进程退出的时候,内核释放该进程所有的资源、包括打开的文件、占用的内存等。但是仍然为其保留一定的信息,这些信息主要主要指进程控制块的信息(包括进程号、退出状态、运行时间等)。
  2. 调用 wait() 函数的进程会挂起(阻塞),直到它的一个子进程退出或收到一个不能被忽视的信号时才被唤醒(相当于继续往下执行)。
  3. 若调用进程没有子进程,该函数立即返回;若它的子进程已经结束,该函数同样会立即返回,并且会回收那个早已结束进程的资源。
  4. 所以,wait()函数的主要功能为回收已经结束子进程的资源。

waitpid

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

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

参数
pid:

  1. pid > 0:等待其进程ID与pid相等的子进程
  2. pid = 0:等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。
  3. pid= -1:等待任意一个子进程。c此时与wait等效
  4. pid < -1:等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。

options:
0同 wait(),阻塞父进程,等待子进程退出。
WNOHANG: 若pid指定的子进程没有结束,则 waitpid()函数立即返回0,不予以等待。若进程正常结束,则返回该子进程的ID。
WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(很好用,知道有这个东西即可)

返回值:
1.当正常返回的时候waitpid返回收集到的子进程的进程ID;
2. 如果设置了选项 WNOHANG,而调用中 waitpid发现没有已退出的子进程可收集,则返回0;
3. 如果调用中出错,则返回-1,这时 errno会被设置成相应的值以指示错误所在;(如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用当前进程的子进程,waitpid() 就会出错返回,这时 errno 被设置为 ECHILD)

statues:
WIFEXITED( status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(sauS): 若 WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

status: 进程退出时的状态信息。

1.如果参数 status 的值不是 NULL,wait() 就会把子进程退出时的状态取出并存入其中,这是一个整数值(int),指出了子进程是正常退出还是被非正常结束的。
2.在wait的参数中存储了子进程的推出原因以及退出码,虽然退出状态用了四个字节存储,而参数中只用了低16位两个字节用于存储这些信息。在这低16位中,高8位存储的是子进程的退出码,只有子进程运行完毕退出时才会有,低8位为0。低7位存储子进程引起异常退出的信号值,第8位存储core dump,只有子进程异常退出时才会有,高8位为0。
status & 0x7f :判断是否正常退出,并获取退出信号
(status >> 8)& 0xff :获取正常退出码
status & 0x7f:获取异常退出的终止信号值

在这里插入图片描述

另外,这个退出信息在一个 int 中包含了多个字段,直接使用这个值是没有意义的,我们需要用宏定义取出其中的每个字段。

下面我们来介绍其中最常用的两个宏定义,取出子进程的退出信息:
WIFEXITED(status): 如果子进程是正常终止的,取出的字段值非零。

WEXITSTATUS(status): 返回子进程的退出状态,退出状态保存在 status 变量的 8~16 位。在用此宏前应先用宏 WIFEXITED 判断子进程是否正常退出,正常退出才可以使用此宏。
也就是说,这两个宏就对应上面的前两种方法

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
int main(int argc, char *argv[])
{
	pid_t pid;
	
	pid = fork(); // 创建进程
	if( pid < 0 ){ // 出错
		perror("fork");
		exit(0);
	}
	
	if( pid == 0 ){// 子进程
		int i = 0;
		for(i=0;i<5;i++)
		{
			printf("this is son process\n");
			sleep(1);
		}
		
		_exit(2); // 子进程退出,数字 2 为子进程退出的状态
		
	}else if( pid > 0){ // 父进程
		
		int status = 0;
		
		// 等待子进程结束,回收子进程的资源
		// 此函数会阻塞
		// status 某个字段保存子进程调用 _exit(2) 的 2,需要用宏定义取出
		wait(&status); 
		// waitpid(-1, &status, 0); // 和 wait() 没区别,0:阻塞
		// waitpid(pid, &status, 0); // 指定等待进程号为 pid 的子进程, 0 阻塞
		// waitpid(pid, &status, WNOHANG); // WNOHANG:不阻塞
		if(WIFEXITED(status) != 0){ // 子进程是否正常终止
			printf("son process return %d\n", WEXITSTATUS(status));
		}
		
		printf("this is father process\n");	
	}
	
	return 0;
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/shanghx_123/article/details/83549815