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 查找有什么信号可以使应用程序暂停


目录
相关文章
|
23天前
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
99 2
|
13天前
|
算法 调度 UED
操作系统中的进程管理:原理与实践
在数字世界的心脏跳动着无数进程,它们如同细胞一般构成了操作系统的生命体。本文将深入探讨进程管理的奥秘,从进程的诞生到成长,再到最终的消亡,揭示操作系统如何协调这些看似杂乱无章却又井然有序的活动。通过浅显易懂的语言和直观的比喻,我们将一起探索进程调度的策略、同步机制的重要性以及死锁问题的解决之道。准备好跟随我们的脚步,一起走进操作系统的微观世界,解锁进程管理的秘密吧!
24 6
|
22天前
|
监控 算法 调度
探索操作系统中的进程管理:从理论到实践
【8月更文挑战第30天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅管理着硬件资源,还确保了软件的顺畅运行。本文将深入探讨操作系统中的一项核心功能——进程管理。我们将从基本概念出发,逐步深入到进程状态、调度算法,以及进程同步机制。通过实际代码示例,我们将看到理论如何转化为实践中的具体操作,从而更好地理解进程管理的精妙之处。无论你是初学者还是有一定基础的开发者,这篇文章都将为你揭开操作系统进程管理的神秘面纱。
|
7天前
|
存储 监控 安全
探究Linux操作系统的进程管理机制及其优化策略
本文旨在深入探讨Linux操作系统中的进程管理机制,包括进程调度、内存管理以及I/O管理等核心内容。通过对这些关键组件的分析,我们将揭示它们如何共同工作以提供稳定、高效的计算环境,并讨论可能的优化策略。
17 0
|
19天前
|
Unix Linux
linux中在进程之间传递文件描述符的实现方式
linux中在进程之间传递文件描述符的实现方式
|
20天前
|
开发者 API Windows
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
43 0
|
3月前
|
监控 Linux 应用服务中间件
探索Linux中的`ps`命令:进程监控与分析的利器
探索Linux中的`ps`命令:进程监控与分析的利器
|
2月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
2月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
71 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
1月前
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。