Linux:进程等待

进程等待

进程等待指的就是父进程等待子进程退出;为了获取退出子进程返回值;释放退出子进程所有资源;由此可以看出进程等待是非常重要的,前面我们说过,当子进程退出,如果父进程不管不顾,就可能使子进程成为僵尸进程,进而造成内存泄漏,而我们也知道僵尸进程非常难以处理,就算是kill-9也无法将它“消灭”,就像成为僵尸的人一样,对于我们来说他已经死了,不能再被杀死第二次了。而我们想知道父进程派给子进程的任务完成的如何了,如,子进程运行完成,结果对还是不对,或者是否正常退出。此时我们可以通过让父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息。

进程等待的两种方法

1.wait方法`

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
 成功返回被等待进程pid,失败返回-1。(比如:没有退出子进程)
参数:
 输出型参数,获取子进程退出状态,不关心则可以设置成NULL

wait是一个阻塞接口,处理退出的子进程,当然若没有子进程退出,则会一直等待,直到有子进程退出才会调用返回

2.waitpid方法

pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
 当正常返回的时候waitpid返回收集到的子进程的进程ID;
 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
 pid:
 Pid=-1,等待任一个子进程。与wait等效。
 Pid>0.等待其进程ID与pid相等的子进程。
 status:
 WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
 WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
 options:
 WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。

注意:
如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
如果不存在该子进程,则立即出错返回。
在这里插入图片描述

两者比较:
1.wait等待的是任意一个子进程的退出(wait是一个父进程假设没有很多子进程,任意一个退出,都会处理后调用返回)
2.waitpid可以等待指定的子进程,也可以等待任意一个子进程,通过第一个参数确定(第一个参数pid ==-1则表示等待任意进程)
3.wait是一个阻塞接口(如果没有子进程退出,则会一直等待)
4.waitpid可以默认阻塞,也可以设置非阻塞,通过第三个参数确定(option == 0 表示默认阻塞)option == WNOHANG则表示非阻塞
注意:非阻塞操作一般需要循环操作,得判断能否处理,直到成功为止负责负责可能会产生僵尸进程
例如

   #include <stdio.h>
   #include <stdlib.h>
   #include <unistd.h>
   #include <sys/wait.h>
  
   int main(int argc,char *argv[])
     {
    
    
       pid_t pid = fork();
       if(pid == 0)
      {
    
    
        sleep(5);
        exit(0);
     }
      else if(pid > 0)
      {
    
    
        int ret = waitpid(pid,NULL,WNOHANG);
        if(0 == ret)
        {
    
    
          printf("没有子进程退出!\n");                                          
        }
        while(1)
        {
    
    
          printf("正在打麻将!\n");
        }
     }
      return 0;
    }

此时运行这个程序使用ps aux | grep 命令查看会发现子进程变成了僵尸进程
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main(int argc, char *argv[])                                                                                                                            
{
    
    
   pid_t pid = fork();
   if(0 == pid)
   {
    
    
     sleep(5);
     exit(0);
   }
   else if(pid > 0)
   {
    
    
     int status;
     while(waitpid(pid,&status,WNOHANG)== 0){
    
    
       printf("没有子进程退出,再等等!");
       sleep(1);
     }
     while(1)
     {
    
    
       printf("正在打麻将!\n");
       sleep(1);
     }
   }
   return 0;
 }

使用循环处理以后这个问题便解决了
在这里插入图片描述

获取子进程status

无论是wait还是waitpid,都有一个status参数,这个参数是一个输出型参数,由操作系统填充,如果传递NULL,则表示不关心子进程的退休状态,否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特
位):
在这里插入图片描述

前面我们直到一个进程退出场景由正常退出和异常退出两种。
当一个进程正常退出的时候,返回值才是有意义的,否则返回值是没有意义的。所以在获取返回值之前,应该先通过低7位,判断进程是否是正常退出的(正常退出则异常退出信号为0;否则大于0)
在这里插入图片描述
所以判断一个进程是否是正常退出的方法为:取出低7位

status & 0x7f == 0 正常退出
0x7f == 0111 1111

之后再取出低16位中的高八位

(status >> 8) & 0xff

这就是我们要获取的status啦。
代码:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main( void )
{
    
    
 	pid_t pid;
 	if ( (pid=fork()) == -1 )
 	perror("fork"),exit(1);
	if ( pid == 0 ){
    
    
	sleep(20);
	exit(10);
 } else 
 {
    
    
 	int st;
 	int ret = wait(&st);
 
 	if ( ret > 0 && ( st & 0X7F ) == 0 ){
    
     // 正常退出
	 printf("child exit code:%d\n", (st>>8)&0XFF);
 } else if( ret > 0 ) {
    
     // 异常退出
	 printf("sig code : %d\n", st&0X7F );
	 }
 	}
}

运行结果:
在这里插入图片描述
上面的结果为运行两次,然后分别比较有没有在其它中端对该进程进行kill操作的结果,上面的为没有进行kill操作,下面的为进行kill操作的结果,如果是使用9号指令,那么结果为sig code:9

猜你喜欢

转载自blog.csdn.net/qq_43825377/article/details/113826404