Linux 进程(九) 进程等待

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

        进程等待的方法:

#include
#include
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
#include
#include
#include
#include


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之后,父进程和子进程谁先运行是不确定的,由调度器说了算。

Linux 进程(九) 进程等待_第1张图片

        修改一下代码,使用waitpid。

        

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

Linux 进程(九) 进程等待_第2张图片

 Linux 进程(九) 进程等待_第3张图片

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

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

Linux 进程(九) 进程等待_第4张图片

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

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
#include
#include
#include
#include

#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;
}

你可能感兴趣的:(linux,linux)