UNIXC002 进程资源的回收、孤儿进程和僵尸进程

进程的资源回收

  • 进程终止以后,如果父进程不来回收子进程的资源,相当于子进程的用户态已经结束了,但是内核态没有,子进程的PCB还在占用着资源。这个PCD有时被称为进程的僵尸(白白占用系统的内存)。所以在子进程结束以后要及时的回收子进程的僵尸。

1. wait

UNIXC002 进程资源的回收、孤儿进程和僵尸进程_第1张图片

1.1 可以使用以下 宏检测进程终止的原因

UNIXC002 进程资源的回收、孤儿进程和僵尸进程_第2张图片

$ man 2 wait
....
       WIFEXITED(status)
              returns true if the child terminated normally, that is, by calling exit(3) or _exit(2), or by returning from main().

       WEXITSTATUS(status)
              returns  the exit status of the child.  This consists of the least significant 8 bits of the status argument that the child specified
              in a call to exit(3) or _exit(2) or as the argument for a return statement in main().  This macro should be employed only  if  WIFEX‐
              ITED returned true.

       WIFSIGNALED(status)
              returns true if the child process was terminated by a signal.

       WTERMSIG(status)
              returns  the  number  of  the  signal  that caused the child process to terminate.  This macro should be employed only if WIFSIGNALED
              returned true.
      
....

1.2 代码示例

wait.c

#include 
#include "t_stdio.h"
#include 
#include 
#include 
int main(void){
     
    int s;
    //创建子进程
    pid_t pid = fork();
    if(pid==-1)E_MSG("fork", -1);
    if(pid == 0){
     
        //子进程执行代码
        printf("child process ... %d\n", getpid());
        getchar();
        // 终止子进程
        exit(-1);
    } else {
     
        // 父进程执行代码
        // wait 阻塞等待子进程结束, 回收子进程资源, 所以子进程执行完以后才会执行下面的printf的代码, 这样就从之前的异步,变成了现在的同步
        // s用来存储子进程的退出状态码
        wait(&s);
        if(WIFEXITED(s)){
        // WIFEXITED(s) 为真说明子进程正常退出,WEXITSTATUS(s)可以获取子进程退出状态码
            printf("exit: %d\n", WEXITSTATUS(s));
        }
        if(WIFSIGNALED(s)){
      // WIFSIGNALED(s) 为真说明进程是被信号打断的,  WTERMSIG(s) 可以获取打断进程信号的编号
            printf("signum: %d\n", WTERMSIG(s));
        }
        printf("father process ...\n");
    }
    return 0;
}
  • 子进程正常退出
    在这里插入图片描述
  • 子进程被信号打断
    在这里插入图片描述

    在这里插入图片描述

2. waitpid

UNIXC002 进程资源的回收、孤儿进程和僵尸进程_第3张图片

  • wait 是等待任意子进程,只要是子进程终止那么就把它给回收
  • waitpid 可以指定某一个子进程的pid,也可以指定某一个进程组的pid

2.1 pid 参数取值说明

UNIXC002 进程资源的回收、孤儿进程和僵尸进程_第4张图片

  • pid == -1, option==0waitpid 完全等同于 wait
  • 进程组包含多个进程,每个进程组里都有leader进程,leader进程的pid就是这个进程组的pid
  • 一般情况下父进程调用fork创建子进程以后,不调用setpgid,那么子进程和父进程都是同组的,如果调用了setpgid, 那么子进程将会被设置为另一个进程组的进程。

waitpid.c

#include 
#include "t_stdio.h"
#include 
#include 
#include 
int main(void){
     
    int s;
    //创建子进程
    pid_t pid = fork();
    if(pid==-1)E_MSG("fork", -1);
    if(pid == 0){
     
        //子进程执行代码
        printf("child process ... %d\n", getpid());
        getchar();
        // 终止子进程
        exit(-1);
    } else {
     
        // 父进程执行代码
        // waitpid(-1, &s, 0); 这是非阻塞模式收子进程的资源. 阻塞: 如果要回收的子进程还没有结束, 等着结束回收资源以后再去执行下面的
        // waitpid(-1, &s, WNOHANG); 这是非阻塞模式收子进程的资源。非阻塞: 如果要回收的子进程还没有结束,waitpid直接返回0, 不等子进程了
        int w=waitpid(-1, &s, WNOHANG);
        if(w==0){
     // 没有子进程的资源回收,直接结束
            printf("parent exit ...\n");
            return 0;
        }
        // 返回值不是0,说明有子进程资源的回收, 再根据返回的状态码来判断子进程是怎么终止的
        if(WIFEXITED(s)){
        // WIFEXITED(s) 为真说明子进程正常退出,WEXITSTATUS(s)可以获取子进程退出状态码
            printf("exit: %d\n", WEXITSTATUS(s));
        }
        if(WIFSIGNALED(s)){
      // WIFSIGNALED(s) 为真说明进程是被信号打断的,  WTERMSIG(s) 可以获取打断进程信号的编号
            printf("signum: %d\n", WTERMSIG(s));
        }
        printf("father process ...\n");
    }
    return 0;
}
# 父进程没有等子进程直接终止
$ ./a.out 
parent exit ...
child process ... 30610

孤儿进程和僵尸进程

UNIXC002 进程资源的回收、孤儿进程和僵尸进程_第5张图片

  • 上图的init 改为 upstart

1. 孤儿进程代码示例

#include "t_stdio.h"
#include 
#include 

int main(void){
     
    pid_t pid = fork();
    
    if(pid == -1)E_MSG("fork", -1);
    if(pid == 0){
     
        // 在子进程中执行的代码
        printf("父进程还没终止时的pid: %d", getppid());
        printf("子进程的pid: %d\n", getpid()); 
        sleep(2);
        printf("父进程终止以后,但是子进程没有终止情况下,父进程的pid: %d", getppid());  
        
    } else {
     
        // 在父进程中执行的代码
        sleep(1);
        printf("parent process ...\n");
    }

    return 0;
}
$ ./a.out 
父进程还没终止时的pid: 27663子进程的pid: 27664
parent process ...
$ 父进程终止以后,但是子进程没有终止情况下,父进程的pid: 2524
$ ps -aux| grep 2524
moonx     2524  0.0  0.0  48216  2512 ?        Ss   11月23   0:19 /sbin/upstart --user
moonx    28021  0.0  0.0  15968  1080 pts/21   S+   19:23   0:00 grep --color=auto 2524

2. 僵尸进程代码示例

#include 
#include "t_stdio.h"
#include 
#include 
#include 
int main(void){
     
    int s;
    //创建子进程
    pid_t pid = fork();
    if(pid==-1)E_MSG("fork", -1);
    if(pid == 0){
     
        //子进程执行代码
        exit(-1);
    } else {
     
        // 父进程执行代码
        getchar();
        wait(NULL);
        
    }
    return 0;
}
$ ./a.out

## 再打开一个shell,子进程 [a.out]  处于僵尸状态 Z+
$ ps -aux| grep a.out
moonx    32381  0.0  0.0   4356   600 pts/21   S+   19:30   0:00 ./a.out
moonx    32382  0.0  0.0      0     0 pts/21   Z+   19:30   0:00 [a.out] <defunct>
moonx    32410  0.0  0.0  15964   980 pts/31   S+   19:30   0:00 grep --color=auto a.out

你可能感兴趣的:(C,UNIX_C)