[리눅스 시스템 프로그래밍] 23. 고아 프로세스, 좀비 프로세스, wait, waitpid

목차

고아 프로세스

테스트 코드 1

시험 결과

좀비 프로세스

테스트 코드 2

시험 결과

기다리다

매개변수 *wstatus

반환 값

테스트 코드 3

시험 결과

테스트 코드 4

시험 결과

테스트 코드 5

시험 결과

기다려

매개변수 pid

매개변수 *wstatus

매개변수 옵션

반환 값

테스트 코드 6

시험 결과

테스트 코드 7

시험 결과

테스트 코드 8

시험 결과

테스트 코드 9

시험 결과

테스트 코드 10

시험 결과

고아 프로세스

        부모 프로세스가 자식 프로세스보다 먼저 종료되면 자식 프로세스는 고아 프로세스가 되고, 자식 프로세스의 부모 프로세스는 init 프로세스가 됩니다. 이를 init 프로세스가 고아 프로세스를 채택한다고 합니다.

테스트 코드 1

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        while(1){
            printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(),getppid());
            sleep(1);
        }
    }
    else if (jin_cheng > 0)
    {
        sleep(3);
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(),jin_cheng);
        printf("父程序结束!\n");
        printf("孤儿进程产生!\n");
    }
    return 0;
}

시험 결과

프로세스의 해당 상위 프로세스를 봅니다.

ps ajx

 

 상위 프로세스가 종료된 후 1319 프로세스에 의해 채택됩니다.

 

좀비 프로세스

        프로세스가 종료되고 상위 프로세스는 재활용되지 않았으며 하위 프로세스의 잔여 자원(PCB)은 커널에 저장되어 좀비 프로세스가 됩니다. 좀비 프로세스는 kill 명령을 사용하여 제거할 수 없습니다. kill 명령은 프로세스를 종료할 때만 사용되어 좀비 프로세스가 종료되었기 때문입니다.

테스트 코드 2

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        sleep(3);
        printf("这是子进程,当前进程的ID是%d,父进程ID是%d。\n", getpid(), getppid());
        printf("子进程结束!\n");
        printf("僵尸进程产生!\n");
    }
    else if (jin_cheng > 0)
    {
        while (1)
        {
            printf("这是父进程,当前进程的ID是%d,子进程ID是%d。\n", getpid(), jin_cheng);
            sleep(1);
        }
    }
    return 0;
}

시험 결과

 6138 프로세스가 종료되어 좀비 프로세스가 되어 상위 프로세스가 재활용되기를 기다리고 있습니다.

 좀비 프로세스를 제거하려면 상위 프로세스를 종료하면 됩니다.

kill -9 6137

 

기다리다

        프로세스가 종료되면 모든 파일 설명자를 닫고 사용자 공간에 할당된 메모리를 해제하지만 PCB는 여전히 예약되어 있으며 커널은 여기에 일부 정보를 저장합니다. 정상적으로 종료되면 종료 상태를 저장하고 종료되면 비정상적으로 어떤 신호로 인해 프로세스가 종료되었는지 저장합니다. 이 프로세스의 상위 프로세스는 wait 또는 waitpid를 호출하여 이 정보를 얻은 다음 이 프로세스를 완전히 지울 수 있습니다.

        상위 프로세스는 하위 프로세스 종료 정보를 재활용하기 위해 대기 함수를 호출합니다. 이 기능에는 세 가지 기능이 있습니다.

  1. 자식 프로세스가 종료되기를 차단하고 기다리는 것은 자식 프로세스가 죽을 때까지 기다리는 것과 같습니다.

  2. 자식 프로세스의 나머지 리소스를 회수합니다.

  3. 자식 프로세스의 종료 상태를 가져옵니다(종료 이유).

        프로세스가 종료되면 운영 체제의 암시적 재활용 메커니즘은 다음과 같습니다.

  1. 모든 파일 설명자를 닫습니다.

  2. 사용자 공간에 할당된 메모리를 해제합니다.

        프로세스의 종료 상태가 저장되는 커널의 PCB는 여전히 존재합니다. 대기 기능을 사용하여 프로세스의 종료 상태를 저장하기 위해 매개변수 상태를 전달할 수 있습니다. 매크로 기능을 사용하여 프로세스 종료에 대한 구체적인 이유를 추가로 확인하십시오.

 프로세스가 정상적으로 종료되었습니다.

WIFEXITED(status)!=0

 프로세스가 정상적으로 종료될 때 사용할 수 있는 프로세스의 종료 상태(exit 매개변수)를 가져옵니다.

WEXITSTATUS(status)

 프로세스가 비정상적으로 종료되었습니다.

WIFSIGNALED(status)!=0

 프로세스가 비정상적으로 종료될 때 사용할 수 있는 프로세스 종료를 유발하는 시그널 번호를 가져옵니다.

WTERMSIG(status)

 프로세스가 일시 중지되었습니다.

WIFSTOPPED(status)!=0

 프로세스가 일시 중단될 때 사용할 수 있는 프로세스를 일시 중단시킨 신호의 번호를 가져옵니다.

WSTOPSIG(status)

 프로세스가 일시 중단된 후에도 계속 실행되었습니다.

WIFCONTINUED(status)!=0

 부모 프로세스는 훌륭하고 자식 프로세스의 사망 원인에 대해 신경 쓰지 않으며 매개 변수는 NULL로 전달됩니다.

ZiJinCheng_ID = wait(NULL);
man 2 wait

매개변수 *wstatus

나가는 매개변수, 재활용 프로세스의 상태입니다.

반환 값

성공: 정리된 자식의 프로세스 ID입니다.

실패: -1, 하위 프로세스가 없습니다.

테스트 코드 3

프로세스를 재활용하려면 대기를 사용하세요.

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

int main(int argc, char *argv[])
{
    int i, wstatus;
    pid_t ZiJinCheng_ID; //子进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        for (i = 0; i < 3; i++)
        {
            printf("这是子进程,当前进程的ID是%d,父进程ID是%d,延时%d秒。\n", getpid(), getppid(), i + 1);
            sleep(1);
        }
        printf("子进程结束!\n");
        printf("僵尸进程产生!\n");
    }
    else if (jin_cheng > 0)
    {
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
        ZiJinCheng_ID = wait(&wstatus);
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
        printf("父程序结束!\n");
    }
    return 0;
}

시험 결과

테스트 코드 4

하위 프로세스의 정상적인 종료를 확인합니다.

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

int main(int argc, char *argv[])
{
    int i, wstatus;
    pid_t ZiJinCheng_ID; //子进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        for (i = 0; i < 3; i++)
        {
            printf("这是子进程,当前进程的ID是%d,父进程ID是%d,延时%d秒。\n", getpid(), getppid(), i + 1);
            sleep(1);
        }
        printf("子进程结束!\n");
        printf("僵尸进程产生!\n");
        return 100;
    }
    else if (jin_cheng > 0)
    {
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
        ZiJinCheng_ID = wait(&wstatus); //子进程未结束,父进程阻塞在这个函数上
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
        if (WIFEXITED(wstatus)) //判断子进程是否正常结束
        {
            printf("子进程正常死亡,死亡原因:%d。\n", WEXITSTATUS(wstatus));
        }
        if (WIFSIGNALED(wstatus)) //判断子进程是否异常结束
        {
            printf("子进程被杀,终止信号是%d。\n", WTERMSIG(wstatus));
        }
        printf("父程序结束!\n");
    }
    return 0;
}

시험 결과

테스트 코드 5

자식 프로세스의 비정상 종료를 봅니다.

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

int main(int argc, char *argv[])
{
    int i, wstatus;
    pid_t ZiJinCheng_ID; //子进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    pid_t jin_cheng = fork();
    if (jin_cheng < 0)
    {
        perror("创建进程错误!");
        exit(1);
    }
    else if (jin_cheng == 0)
    {
        while(1)
        {
            printf("这是子进程,当前进程的ID是%d,父进程ID是%d,我是长生不老的。\n", getpid(), getppid());
            sleep(1);
        }
    }
    else if (jin_cheng > 0)
    {
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程开始等待收尸!\n", getpid(), jin_cheng);
        ZiJinCheng_ID = wait(&wstatus); //子进程未结束,父进程阻塞在这个函数上
        printf("这是父进程,当前进程的ID是%d,子进程ID是%d,父进程收尸完成,僵尸进程ID是%d。\n", getpid(), jin_cheng, ZiJinCheng_ID);
        if (WIFEXITED(wstatus)) //判断子进程是否正常结束
        {
            printf("子进程正常死亡,死亡原因:%d。\n", WEXITSTATUS(wstatus));
        }
        if (WIFSIGNALED(wstatus)) //判断子进程是否异常结束
        {
            printf("子进程被杀,终止信号是%d,还说是长生不老,直接把你给杀了。\n", WTERMSIG(wstatus));
        }
        printf("父程序结束!\n");
    }
    return 0;
}

시험 결과

기다려

재활용 프로세스를 지정합니다.

man 2 waitpid

 

매개변수 pid

0보다 큼: 재활용된 하위 프로세스의 ID를 지정합니다.

0: waitpid에 대한 현재 호출과 동일한 그룹의 모든 하위 프로세스를 재활용합니다.

-1: 모든 하위 프로세스를 재활용합니다.

-1 미만: 지정된 프로세스 그룹의 모든 하위 프로세스를 재활용합니다.

매개변수 *wstatus

나가는 매개변수, 재활용 프로세스의 상태입니다.

매개변수 옵션

WNOHANG: 재활용 방법이 비차단임을 지정합니다.

0: 블록 재활용, 기능은 대기 기능과 동일합니다.

반환 값

0보다 큼: 하위 프로세스의 PID가 성공적으로 재활용됩니다.

0: 함수가 호출될 때 매개변수 옵션이 WNOHANG을 지정하고 하위 프로세스가 종료되지 않습니다.

-1: 실패했습니다.

테스트 코드 6

지정된 하나의 프로세스를 비차단 방식으로 재활용합니다. 상위 프로세스는 지연되지 않고 하위 프로세스는 종료되지 않으며 0을 반환합니다.

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

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t JinCheng_ID;         //子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
        if (i == 2)
        {
            JinCheng_ID = temp_ZiJinCheng_ID;
        }
    }
    if (i == 5)
    {
        HuiShou_JinCheng_ID=waitpid(JinCheng_ID,NULL,WNOHANG);//以非阻塞的方式回收一个进程ID,JinChen_ID
        if(HuiShou_JinCheng_ID<0){
            perror("回收进程错误");
            exit(1);
        }
        printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(),HuiShou_JinCheng_ID);
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
    }
    return 0;
}

시험 결과

테스트 코드 7

비 차단 방식으로 지정된 하나의 프로세스를 재활용합니다. 상위 프로세스가 지연되고 하위 프로세스가 종료되며 복구된 프로세스 ID를 반환합니다.

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

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t JinCheng_ID;         //子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
        if (i == 2)
        {
            JinCheng_ID = temp_ZiJinCheng_ID;
        }
    }
    if (i == 5)
    {
        sleep(5); //延时,最后输出父进程
        HuiShou_JinCheng_ID = waitpid(JinCheng_ID, NULL, WNOHANG); //以非阻塞的方式回收一个进程ID,JinChen_ID
        if (HuiShou_JinCheng_ID < 0)
        {
            perror("回收进程错误");
            exit(1);
        }
        printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
    }
    return 0;
}

시험 결과

테스트 코드 8

임의의 하위 프로세스를 재활용합니다.

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

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
    }
    if (i == 5)
    {
        sleep(6);	//延时,确保所有子进程都已经结束,最后输出父进程
        HuiShou_JinCheng_ID = waitpid(-1, NULL, WNOHANG); //以非阻塞的方式回收任意一个子进程ID
        if (HuiShou_JinCheng_ID < 0)
        {
            perror("回收进程错误");
            exit(1);
        }
        printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
    }
    return 0;
}

시험 결과

        온라인 튜토리얼에는 모든 하위 프로세스를 재활용하라고 나와 있지만 결과를 보면 매번 종료되는 첫 번째 하위 프로세스가 재활용됩니다. 최신 버전의 Ubuntu 시스템이 재활용 프로세스의 알고리즘을 최적화했다는 가능성을 배제할 수는 없습니다. 첫 번째 끝이 가장 먼저 재활용된다는 것을 깨닫습니다.

 

테스트 코드 9

차단 방식으로 모든 죽은 하위 프로세스를 재활용합니다.

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

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
    }
    if (i == 5)
    {
        while ((HuiShou_JinCheng_ID = waitpid(-1, NULL, 0)) != -1) //以阻塞的方式回收任意一个子进程ID
        {
            printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
        }
        if (HuiShou_JinCheng_ID < 0)
        {
            perror("这是父进程,回收子进程完成,父进程结束!\n");
        }
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d。\n", i + 1, getpid());
    }
    return 0;
}

시험 결과

테스트 코드 10

비차단 방식으로 모든 죽은 하위 프로세스를 재활용합니다.

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

int main(int argc, char *argv[])
{
    int i;
    pid_t temp_ZiJinCheng_ID;  //临时子进程ID
    pid_t HuiShou_JinCheng_ID; //回收进程ID
    printf("程序开始运行!\n");
    printf("当前进程的ID是%d。\n", getpid());
    printf("开始创建进程!\n");
    for (i = 0; i < 5; i++)
    {
        temp_ZiJinCheng_ID = fork();
        if (temp_ZiJinCheng_ID == 0) //子进程,不参与创建进程
        {
            break;
        }
    }
    if (i == 5)
    {
        sleep(6);                                                  //延时,确保所有的子进程都死亡
        while ((HuiShou_JinCheng_ID = waitpid(-1, NULL, WNOHANG)) != -1) //以非阻塞的方式回收任意一个子进程ID
        {
            printf("这是父进程,当前进程的ID是%d,回收的子进程是%d。\n", getpid(), HuiShou_JinCheng_ID);
        }
        if (HuiShou_JinCheng_ID < 0)
        {
            perror("这是父进程,回收子进程完成,父进程结束!\n");
        }
    }
    else
    {
        sleep(i); //延时i秒,按顺序输出
        printf("这是第%d个子进程,当前进程的ID是%d,该进程结束。\n", i + 1, getpid());
    }
    return 0;
}

시험 결과

 

추천

출처blog.csdn.net/CETET/article/details/132267131