孤儿进程和僵尸进程

参考:https://baike.baidu.com/item/waitpid/4071590
一、孤儿进程
1、概念:父进程已经结束,子进程却没有结束。
2、危害:父进程用fork创建新的子进程之后,两者是独立的。父进程负责回收子进程的退出状态信息和分配的资源。如果父进程先结束,则子进程没有父进程进行资源的回收,这样会造成资源的浪费,而且子进程会遗留PCB等待父进程的处理,这样也造成了内存的浪费,因为操作系统限制了进程号个数,但是未处理的子进程PCB会占用这些资源。
3、解决方法:操作系统考虑到了这个问题,所以将孤儿进程的PPID设置为1,也就是init进程。init进程接管了该子进程,并等待它结束。
二、僵尸进程
1、概念:父进程未结束,子进程已经结束,内核并没有立即释放该子进程的进程表表项(即PCB),因为考虑到父进程可能还需要查看该子进程的退出信息。
2、危害:
      1)操作系统限制了可以加载进内存的进程个数,操作系统也会每个加载进内存的进程分配一个唯一的PID,但是PID的个数有限,如果你僵尸进程不处理,它的PCB还在,僵尸进程一直占用着进程号,但是其实它已经结束了,如果这样的进程多了,可能新的进程想进来,却因为僵尸进程受到了阻碍。
      2)僵尸进程的出现时因为父进程没有及时获取并处理子进程,父进程负责回收子进程的资源,如果父进程不及时处理子进程的信息,子进程的资源得不到回收释放,则会造成资源的极大浪费,同时PCB中所携带的信息,也占用了内存,如果僵尸进程多了,也会造成内存的极大浪费。
3、解决方法
1)wait函数
  作用:处理任意一个僵尸进程。
  缺点:这个函数是阻塞运行的,父进程调用wait函数意味着父进程将暂停它的工等待一个僵尸进程处理完成再继续进行它原来的工作。
  原型:pid_t wait(int *status);
  返回值:返回所处理的僵尸进程的PID;
  status:可以将僵尸进程的退出状态带出来。
  退出状态:WIFEXITED(*status) 为非零,表示进程正常退出。
            WIFSTOPPED(*status)为非零,表示进程暂停退出。
            WIFSIGNALED(*status)为非零,表示进程异常退出。
            WIFCONTINUED(*status)为非零,表示暂停后已经继续进行。
代码:
  //用pid_t都需要包含的头文件
  #include
  #include
  #include
int main()
{
    pid_t n=fork();
if(n==0)
   {
      sleep(4);
      printf("child over\n");
   }
else
  {
     wait(NULL);//因为我们这里不需要知道它的状态;
  }
}
这里我们的调用wait的父进程,执行wait时,会等待子进程执行完成,也就是childover 输出之后它才结束。
2)waitpid函数
  作用:处理僵尸进程,但是它比wait更强大。
  原型:pid_t waitpid(pid_t pid,int *status,int options);
  返回值:当正常返回的时候,waitpid返回收集到的子进程的进程ID;
          如果设置了WNOHANG,而调用中waitpid发现没有已退出的子进程可收集则返回0;
          如果调用中出错,则返回-1,这是errno会被设置成相应的值以指示错误
所在;
参数:pid  
pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。
pid=-1 等待任何子进程,相当于 wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为 pid 的子进程。
参数options提供了一些额外的选项来控制waitpid,参数 option 可以为 0 或可以用"|"运算符把它们连接起来使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我们不想使用它们,也可以把options设为0,如:
ret=waitpid(-1,NULL,0);
WNOHANG 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。
WUNTRACED 若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。WIFSTOPPED(status)宏确定返回值是否对应与一个暂停子进程。
子进程的结束状态返回后存于 status,底下有几个宏可判别结束情况:
WIFEXITED(status)如果若为正常结束子进程返回的状态,则为真;对于这种情况可执行WEXITSTATUS(status),取子进程传给exit或_eixt的低8位。
WEXITSTATUS(status)取得子进程 exit()返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)若为异常结束子进程返回的状态,则为真;对于这种情况可执行WTERMSIG(status),取使子进程结束的信号编号。
WTERMSIG(status) 取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status) 若为当前暂停子进程返回的状态,则为真;对于这种情况可执行WSTOPSIG(status),取使子进程暂停的信号编号。
WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。
如果执行成功则返回子进程识别码(PID) ,如果有错误发生则返回
返回值-1。失败原因存于 errno 中。

3)信号
   子进程在结束的的时候会给父进程发送一个SIGCHLD,如果这个时候我们调用wait就实现了异步处理僵尸进程,而不用阻塞等待了,父进程可以先干自己的事情,等到子进程结束发出信号之后再直接处理。
    这里就要讲到一个信号的绑定函数signal
    原型:typedif void (*Fun_Handle)(int)
          FUN_Handle signal(int signum,Fun_Handle fun);
    功能:完成signum参数执行的信号与fun函数绑定,当接收到signum这个信号,
执行这个函数。
函数代码实现:
#include
#include
#include
int fun(int sign)
{
   pid_t n=wait(NULL);
   printf("pid:%d",n);
}
int main()
{
  signal(SIGCHLD,fun)
  pid_t n=fork();
  if(n==0)
 {
    printf("child begins\n");
    printf("child over\n");
  }
  else
  { 
     while(1)
     {
        printf("helloworld\n");
      }
  }
}
大家会发现开始helloworld照常输出,只有child over输出完后,输出子进程pid,
然后再继续输出helloworld,说明它们的处理是异步的

猜你喜欢

转载自blog.csdn.net/betty2017/article/details/78696772