Linux进程实践(4) --wait避免僵尸进程

简介: Wait的背景   当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止)   子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态。

Wait的背景

   当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止)

   子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程,它只保留最小的一些内核数据结构,以便父进程查询子进程的退出状态

   父进程查询子进程的退出状态可以用wait/waitpid函数

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

Wait

pid_t wait(int *status);

  当我们用fork启动一个进程时,子进程就有了自己的生命,并将独立地运行。有时,我们需要知道某个子进程是否已经结束了,这样我们可以通过wait安排父进程在子进程结束之后。

  status:该参数可以获得你等待子进程的信息

返回值:

  on success, returns the process ID of the terminated child;  on  error,  -1  is returned.

特征:

  1.wait系统调用会使父进程暂停执行,直到它的任意一个(并不是所有的)子进程结束为止。

  2.返回的是子进程的PID,它通常是已经结束了的子进程;

  3.状态信息允许父进程判定子进程的退出状态,即从子进程的main函数返回的值或子进程中exit/_exit语句的退出码。

  4.如果status不是一个空指针,状态信息将被写入它指向的位置

//示例
int main()
{
    pid_t pid = fork();
    if (pid == -1)
        err_exit("fork");
    else if (pid == 0)
    {
        cout << "In Child, pid = " << getpid() << endl;
        sleep(5);
        exit(10);
    }

    int status;
    int returnPid = wait(&status);
    //两个pid的值相同,但是status的值根本不是10
    cout << "In Parent, returnPid = " << returnPid
         << ", status = " << status << endl;
}


Wait获取status

宏定义

描述

WIFEXITED(status)

     WEXITSTATUS(status)

如果子进程正常结束,返回一个非零值

    如果WIFEXITED非零,返回子进程退出码

WIFSIGNALED(status)

     WTERMSIG(status)

子进程因为捕获信号而终止,返回非零值

    如果WIFSIGNALED非零,返回信号代码

WIFSTOPPED(status)

     WSTOPSIG(status)

如果子进程被暂停,返回一个非零值

    如果WIFSTOPPED非零,返回一个信号代码

//示例
void printStatus(int status)
{
    if (WIFEXITED(status))
    {
        cout << "normal termination, exit status = " << WEXITSTATUS(status) << endl;
    }
    else if (WIFSIGNALED(status))
    {
        cout << "abnormal termination, signal number = " << WTERMSIG(status);
#ifdef WCOREDUMP
        if (WCOREDUMP(status))
            cout << " (core file generated)" << endl;
#else
        cout << endl;
#endif // WCOREDUMP
    }
    else if (WIFSTOPPED(status))
    {
        cout << "child stopped, status number = " << WSTOPSIG(status) << endl;
    }
    else
    {
        cout << "Unknow Stop!" << endl;
    }
}

//测试代码
int main()
{
    pid_t pid = fork();
    if (pid == -1)
        err_exit("fork");
    else if (pid == 0)
        exit(7);

    int status;
    if (wait(&status) == -1)
        err_exit("first wait error");
    printStatus(status);

    pid = fork();
    if (pid == -1)
        err_exit("fork");
    else if (pid == 0)
        abort();

    if (wait(&status) == -1)
        err_exit("first wait error");
    printStatus(status);

    pid = fork();
    if (pid == -1)
        err_exit("fork");
    else if (pid == 0)
        status /= 0;

    if (wait(&status) == -1)
        err_exit("first wait error");
    printStatus(status);

    return 0;
}

查看信号值


Waitpid

pid_t waitpid(pid_t pid, int *status,int options)

等待某个特定进程的结束

参数:

Pid:The value of pid can be:

  <-1    meaning wait for any child process whose process group ID is equal to the absolute value of pid.

  -1      meaning wait for any child process(任一子进程).

   0       meaning wait for any child process whose process group ID is equal to that of  the calling process(与调用者进程同在一个组的进程).

  >0     meaning wait for the child whose process ID is equal to the value of pid.

status:如果不是空,会把状态信息写到它指向的位置(同wait)

options:允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起

返回值:

  如果成功返回等待子进程的ID,失败返回-1

 

wait与waitpid的区别:

  1.在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。

  2.waitpid并不等待第一个终止的子进程:它有若干个选择项,可以控制它所等待的特定进程。

  3.wait函数相当于是waitpid函数的一个特例。

waitpid(-1, &status, 0);

 

僵尸进程(如果不等待...)

  当一个子进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行或者父进程调用了wait才告终止。

  进程表中代表子进程的数据项是不会立刻释放的,虽然不再活跃了,可子进程还停留在系统里,因为它的退出码还需要保存起来以备父进程中后续的wait调用使用。它将称为一个“僵尸进程”

 

如何避免僵尸进程

  方法1:调用wait或者waitpid函数查询子进程退出状态。

  方法2:如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的(表明父进程忽略SIGCHLD信号,一切让Linux内核管理)。


-利用man手册,减少开发难度,提高开发技能

如:Man 7 signal 查找有什么信号可以使应用程序暂停


目录
相关文章
|
6天前
|
关系型数据库 MySQL Linux
Linux环境下MySQL数据库自动定时备份实践
数据库备份是确保数据安全的重要措施。在Linux环境下,实现MySQL数据库的自动定时备份可以通过多种方式完成。本文将介绍如何使用`cron`定时任务和`mysqldump`工具来实现MySQL数据库的每日自动备份。
19 3
|
4月前
|
Ubuntu Linux vr&ar
IM跨平台技术学习(十二):万字长文详解QQ Linux端实时音视频背后的跨平台实践
本文详细记录了新版QQ音视频通话在 Linux 平台适配开发过程中的技术方案与实现细节,希望能帮助大家理解在 Linux 平台从 0 到 1 实现音视频通话能力的过程。
166 2
|
25天前
|
监控 Linux 云计算
Linux操作系统在云计算环境中的实践与优化###
【10月更文挑战第16天】 本文探讨了Linux操作系统在云计算环境中的应用实践,重点分析了其在稳定性、安全性和高效性方面的优势。通过具体案例,阐述了Linux如何支持虚拟化技术、实现资源高效分配以及与其他开源技术的无缝集成。文章还提供了针对Linux系统在云计算中的优化建议,包括内核参数调整、文件系统选择和性能监控工具的应用,旨在帮助读者更好地理解和应用Linux于云计算场景。 ###
31 3
|
1月前
|
Ubuntu Linux
Linux实践|设置静态 IP 地址
Linux实践|设置静态 IP 地址
61 0
Linux实践|设置静态 IP 地址
|
1月前
|
Linux C++
Linux c/c++进程之僵尸进程和守护进程
这篇文章介绍了Linux系统中僵尸进程和守护进程的概念、产生原因、解决方法以及如何创建守护进程。
19 0
|
3月前
|
存储 人工智能 数据管理
深入理解Linux操作系统之文件系统管理探索人工智能:从理论到实践的旅程
【8月更文挑战第30天】在探索Linux的无限可能时,我们不可避免地会遇到文件系统管理这一核心话题。本文将深入浅出地介绍Linux文件系统的基础知识、操作命令及高级技巧,帮助你更有效地管理和维护你的系统。从基础概念到实践应用,我们将一步步揭开Linux文件系统的神秘面纱。
|
3月前
|
存储 安全 Linux
Linux存储安全:系统更新和补丁管理的策略与实践
【8月更文挑战第19天】安全是一个持续的过程,需要不断地评估、更新和改进策略。
64 0
|
3月前
|
存储 安全 Linux
Linux存储安全:数据加密的实践与策略
【8月更文挑战第19天】数据加密是Linux存储安全的基石之一。通过使用LUKS进行磁盘加密和使用GnuPG进行文件加密,可以显著提高数据的安全性。
95 0
|
3月前
|
存储 监控 安全
Linux存储安全:访问控制的实践与策略
【8月更文挑战第18天】Linux存储安全:访问控制的实践与策略
52 0
|
3月前
|
存储 安全 Linux
Linux存储安全:深入实践与案例分析
【8月更文挑战第18天】Linux存储安全是一个多层次、多维度的问题,需要从物理安全、访问控制、数据加密、审计监控、系统更新、备份策略等多个方面综合考虑。通过本文介绍的具体措施和案例代码,读者可以更好地理解如何在Linux系统中实施存储安全措施。安全是一个持续的过程,需要不断地评估、更新和改进策略。
81 0