【Linux】回收子进程—wait/waitpid

一、孤儿进程

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

    

#include
#include
#include


int main(void)
{
	pid_t pid;

	pid = fork();

	if( pid == -1 )
	{
		perror("fork");
		exit(1);
	}
	else if( pid > 0)
	{
		printf("I am parent,my pid = %d\n",getpid());

		sleep(9);

		printf("--------------parent going to die------------------\n");
	}
	else
	{
		while(1)
		{
			printf("I am child,my parent pid = %d\n",getppid());

			sleep(1);	
		}
	}

	return 0;
}



【Linux】回收子进程—wait/waitpid_第1张图片

 

        此时程序中的打印信息会一直进行打印,即使你按下 ctrl+c   也不会退出正在执行的程序 ./orpan;此时需要使用 ps -ef ,查看正在打印的程序信息;然后使用 kill -9  + 进程ID(./orphan 的ID)

二、 僵尸进程

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

        注意:

                 僵尸进程是不能使用 kill 命令清除掉的。因为 kill 命令只是用来终止进程的,而僵尸进程已经终止。


#include
#include
#include


int main(void)
{
	pid_t pid;

	pid = fork();

	if( pid == -1 )
	{
		perror("fork");
		exit(1);
	}
	else if( pid > 0)
	{
		printf("I am parent,my pid = %d\n",getpid());

		sleep(9);

		printf("--------------parent going to die------------------\n");
	}
	else
	{
		while(1)
		{
			printf("I am child,my parent pid = %d\n",getppid());

			sleep(1);	
		}
	}

	return 0;
} 



     父子进程打印出的信息如下:

【Linux】回收子进程—wait/waitpid_第2张图片

        可通过 ps -ef 命令来查看进程信息,可以查看出僵尸进程:

  



三、wait 函数

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

      

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

                  (1)、阻塞等待子进程退出

                  (2)、回收子进程残留资源

                  (3)、获取子进程结束状态


          pid_t wait(int *status);        成功:返回清理掉的子进程 ID;            失败:-1(没有子进程) 

        当进程终止时,操作系统的隐式回收机制会:

                  (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)   为真 ------------------->  进程暂停后继续运行

                  

#include
#include
#include
#include
#include


int main(void)
{
	pid_t pid,wpid;

	int status;
	
	pid = fork();

	if( pid == -1 )
	{
		perror("fork");
		exit(1);
	}
	else if( pid == 0)
	{

		execl("abnor","abnor",NULL);
		printf("I am child,my parent pid = %d,going to sleep 10s\n",getppid());

		sleep(20);	
		printf("-------------child die------------------\n");
//		exit(76);
		return 100;
	}

	else
	{
		// 回收子进程,避免出现僵尸进程

		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 exit with %d\n",WTERMSIG(status));	
		}
		while(1)
		{
			printf("I am parent,my pid = %d,my son = %d\n",getpid(),pid);

			sleep(1);
		}
	}
	return 0;
}


四、waitpid 函数

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

  pid_t waitpid(pid_t pid, int *status, int options);  
                    
                    返回值:成功:返回清理掉的子进程 ID;
                            失败:-1(无子进程)
     特殊参数和返回情况:
                
                    参数1 pid:
                          >0       回收指定 ID 的子进程
                          
                          -1       回收任意子进程(相当于 wait)
 
                          =0       回收和当前调用 waitpid 一个组的所有子进程

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

                    参数 2:status
                  
                    参数 3 : 0     <===>  wait  参数为0,等价为 wait ,阻塞回收。
                             
                             WNOHNAG   非阻塞回收(轮询)

                    返回值 :成功:返回 pid   失败:返回 -1
                             
                             返回 0 值: 参 3 传了 WNOHNAG,并且子进程尚未结束。

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


#include
#include
#include
#include
#include


int main(int argc,char *argv[])
{
    int n=5,i;   // 默认创建 5 个子进程
    pid_t p,q;
    pid_t wid;
    if( argc == 2 )
    {
        n=atoi(argv[1]); 
    }

    for( i=0;i0 )
            {
                n--;  
            }
            // if wid == 0 说明子进程正在运行
            sleep(1);
            
        }while( n> 0);

        printf("wait finish\n");
        while(1);
    }
    else 
    {
        sleep(i);
        printf("I am %dth child,pid = %d,gpid = %d\n",i+1,getpid(),getppid());
    }
    return 0;
}

waitpid 的使用:

/*
    需求:
         父进程 fork 3 个子进程,三个子进程 一个调用 ps 命令,一个调用自定义程序1(正常),一个调用自定义程序1(会出错误)。

         父进程会使用 waitpid 对子进程进行回收。
*/


#include
#include
#include
#include
#include

int main(int argc,char *argv[])
{
    int n=3,i;
    pid_t pid;
    pid_t wid;
    for( i=0;i 0 )
            {
                i--;  
            } 
            sleep(1);
        }while( i > 0 ); 

        printf(" wait finnaly!\n");

        sleep(10);
    }

    return 0;
}

你可能感兴趣的:(Linux&&操作系统)