Linux 进程(九) 进程等待

发布时间:2024年01月04日

????????子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏,所以父进程回收子进程是必然要做的。
????????另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
????????最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
????????父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息(子进程退出的信息包括进程退出码,我进程推出时候的信号)。

????????进程等待的方法:

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

????????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

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


int main()
{
   pid_t id = fork();
   if(id == 0)
   {
      int cnt = 5;
      while(cnt--)
      {
        printf(" i am child procrss,pid:%d,ppid:%d\n",getpid(),getppid());
        sleep(1);
      }
      printf("子进程退出\n");
      exit(1);
   }
   printf("父进程开始休眠,等待子进程\n");
   sleep(10);
   printf("父进程开始回收\n");

   pid_t rid = wait(NULL);
   if(rid > 0)
   {
    printf("wait success,rid:%d\n",rid);
   }

    printf("父进程回收结束\n");
    sleep(3);

  return 0;
}

? ? ? ? 该代码,总共运行十三秒,前五秒父进程子进程一起运行,前五秒过后子进程退出。中间五秒父进程等待回收子进程,这时会看到子进程僵尸的状态,中间五秒后,父进程回收子进程。最后三秒,父进程休眠三秒,然后程序退出。

? ? ? ? 注意:fork之后,父进程和子进程谁先运行是不确定的,由调度器说了算。

? ? ? ? 修改一下代码,使用waitpid。

????????

? ? ? ? 通过上文对waitpid的描述,第一个参数传id,是子进程的id。第二个参数传递一个地址,这个记录进程退出时的退出码和收到的信号,第三个参数设置为0表示阻塞等待。

?

? ? ? ? 通过上图我们可以看出,进程推出的退出状态时status = 256 。那么时为什么呢?

? ? ? ? status是一个整形,有三十二个bit位,前16个bit位不用,后十六个bit位的前八位表示进程退出时的退出码,后七位表示进程退出时收到的信号。子进程退出时设置的时exit(1),故而经过转化,得到的status是256。

? ? ? ? 如果想直接拿到信号编号和进程退出码,还可以这样写。

printf("wait success! rid:%d , status:%d,exit singno:%d, exit code:%d\n",rid,status,status&0x7F,(status>>8)&0xFF);

? ? ? ? 这样显示的就比较直观了。

? ? ? ?如果不想着麻烦的话,Linux系统同时给我们提供了两个宏让我们可以直接提取到进程的退出码。

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

? ? ? ? 用法如下:

  if(WIFEXITED(status)){
printf("wait success! rid:%d , status:%d,exit code:%d\n",rid,status,WEXITSTATUS(status));} 

????????

????????同时waitpid的第三个参数设置为0表示阻塞调用,而设置为WNOHANG 表示非阻塞调用。

? ? ? ? 阻塞调用表示:父进程一直等待当前子进程到结束,然后读取返回的退出码以及信号。

? ? ? ? 非阻塞调用表示:父进程基于非阻塞的轮询访问子进程,访问一次子进程发现没有结果,然后就返回,不必等到子进程有结果才返回。其间父进程也可以去执行别的代码,而不是一直卡住等待子进程有结果。

? ? ? ? 注意:为什么我们不能自己定义一个变量,子进程退出修改这个变量,然后给父进程呢?

? ? ? ? 答案是不行的,进程间具有独立性。父进程创建子进程,当子进程对数据做修改时会发生写时拷贝。子进程的信息属于内核数据,父进程读取子进程属于读取内核数据,而操作系统不相信任何人,所以只能通过系统调用来读取。同时,用户定义的变量属于用户层面的,而子进程的信息属于内核数据,子进程不能直接对用户层面的信息做修改,还是要通过系统调用。

? ? ? ? 下面是一个代码演示了基于非阻塞轮询访问子进程,其间父进程也可以执行别的代码。

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

#define NUM 5
typedef void(*fun_t)();
fun_t task[NUM];

void printflog()
{
  printf("this is log task\n");
}

void printfnet()
{
  printf("this is net task\n");
}

void printfnpc()
{
  printf("this is npc task\n");
}

void inittask()
{
  task[0] = printflog;
  task[1] = printfnet;
  task[2] = printfnpc;
  task[3] = NULL;
}

void excutetask()
{
  for(int i = 0;task[i];i++) task[i]();
}

int main()
{
  inittask();
  pid_t id = fork();
  if(id == 0)
  {
    int cnt = 5;
    while(cnt)
    {
      printf("i am child process,pid:%d ,ppid:%d\n",getpid(),getppid());
      sleep(1);
      cnt--;
    }
    exit(111);

  }

  int status = 0;
  while(1)
  {
    pid_t rid = waitpid(id,&status,WNOHANG); 
    if(rid > 0){

      printf("wait success! rid:%d , status:%d,exit  
 code:%d\n",rid,status,WEXITSTATUS(status));
      break;
    }
    else if(rid == 0)
    {
      printf("father say: child is runnin,do other thing\n");
      printf("##################begin######################\n\n");
      excutetask();
      printf("##################end######################\n");
    }
    else{
      perror("waitpid");
      break;
    }
    sleep(1);
  }

  return 0;
}

文章来源:https://blog.csdn.net/kqs__/article/details/135185949
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。