僵死进程(《精通Unix下C语言编程与项目实践》之五)

简介:
《精通Unix下C语言编程与项目实践》之五
僵死进程 
作者:朱云翔,胡平

9.3 僵死进程

僵死进程是已经终止,但没有从进程表中清除的进程。代码9-12展示了一个产生僵死进程的例子:
代码9-12 僵死进程产生模型(节自/code/chapter9/szomb1.c
#include <unistd.h>
#include <stdio.h>
void main()
{
    pid_t pid1;
    if ((pid1 = fork()) == 0)
    {
        printf("child[%d]\n", getpid());
        exit(0);
    }
    /* wait(); */           /*  */
    printf("parent[%d]\n", getpid());
    sleep(60);
}
编译代码9-12
# make szomb1
        cc -O -o szomb1 szomb1.c
后台运行程序szobm1,并在子进程结束后,父进程没有结束前,运行命令查询进程情况:
# ./szomb1&
10570
# child[10571]
parent[10570]
# ps -ef|grep 10570
    root 10570 10449  0 19:25:50   ttyp0    00:00:00 ./szomb1
    root 10571 10570  0        -       -    00:00:00 <defunct>
其中“<defunct>”代表僵死进程(不同版本Unix描述略有不同)。因此可知,子进程并没有如期消失,而是转化为僵死进程继续占用系统进程表资源。如果系统存在大量的僵死进程,就会过多占用进程表空间,影响新进程的产生。对于僵死进程,不能奢望通过kill命令杀死之,因为它已经“死”了,不再接收任何系统信号。
当子进程终止时,它释放资源,并且发送SIGCHLD信号通知父进程。父进程接收SIGCHLD信号,调用wait返回子进程的状态,并且释放系统进程表资源。故如果子进程先于父进程终止,而父进程没有调用wait接收子进程信息,则子进程将转化为僵死进程,直到其父进程结束。
一旦知道了僵死进程的成因,我们就可以采用如下方法预防僵死进程:

1. wait

父进程主动调用(或者接收到SIGC(H)LD信号后调用)wait接收子进程的死亡报告,释放子进程占用的系统进程表资源。如将代码9-10中屏蔽的代码行wait();”打开,就可以有效的阻止僵死进程的产生。

2. 托管法

如果父进程先于子进程而死亡,则它的所有子进程转由进程init领养,即它所有子进程的父进程ID号变为1(进程init的标识为1)。当子进程结束时init为其释放进程表资源。

3. 忽略SIGC(H)LD信号

当父进程忽略SIGC(H)LD信号后,即使不执行wait,子进程结束时也不会产生僵死进程(有关信号的相关知识将在下一章讲述)。

3. 捕获SIGC(H)LD信号

当父进程捕获SIGC(H)LD信号,并在捕获函数代码中等待(wait)子进程。
【实践经验】实现托管法的技巧是调用fork两次,让子进程退出,子子进程的父进程更改为进程init,这样父进程就不必理会子子进程的处理了。代码9-13设计了一个两次fork避免僵死进程的例子。
代码9-13 托管法预防僵死进程(节自/code/chapter9/szomb2.c
#include <unistd.h>
#include <stdio.h>
void main()
{
    pid_t pid1;
    if ((pid1 = fork()) == 0)   /* 第一次fork */
    {
        printf("child[%d]\n", getpid());
        if ((pid1 = fork()) == 0)   /* 第二次fork */
        {
            printf("child[%d]\n", getpid());
            sleep(20);
            exit(0);        /* 第二次创建的子进程退出 */
        }
        exit(0);            /* 第一次创建的子进程退出 */
    }
    waitpid(pid1, 0, 0);
    printf("parent[%d]\n", getpid());
    sleep(60);
}
编译代码9-13
# make szomb2
        cc -O -o szomb2 szomb2.c
后台运行程序szobm2,并在子进程结束前,运行命令查询进程情况:
# ./szomb2&
10731
# child[10732]
child[10733]
parent[10731]
# ps -ef|grep szomb2
    root 10731 10449  0 20:32:19   ttyp0    00:00:00 ./szomb2
    root 10733     1  0 20:32:19   ttyp0    00:00:00 ./szomb2
上例中,进程10733的父进程标识号已经更改为1,即已经由系统进程init(标识号为1)托管。



 本文转自 zhuyunxiang 51CTO博客,原文链接:http://blog.51cto.com/zhuyunxiang/133246,如需转载请自行联系原作者


相关文章
|
1月前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
49 8
|
2月前
|
C语言
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性。本文探讨了C语言中的错误类型(如语法错误、运行时错误)、基本处理方法(如返回值、全局变量、自定义异常处理)、常见策略(如检查返回值、设置标志位、记录错误信息)及错误处理函数(如perror、strerror)。强调了不忽略错误、保持处理一致性及避免过度处理的重要性,并通过文件操作和网络编程实例展示了错误处理的应用。
78 4
|
3月前
|
NoSQL C语言 索引
十二个C语言新手编程时常犯的错误及解决方式
C语言初学者常遇错误包括语法错误、未初始化变量、数组越界、指针错误、函数声明与定义不匹配、忘记包含头文件、格式化字符串错误、忘记返回值、内存泄漏、逻辑错误、字符串未正确终止及递归无退出条件。解决方法涉及仔细检查代码、初始化变量、确保索引有效、正确使用指针与格式化字符串、包含必要头文件、使用调试工具跟踪逻辑、避免内存泄漏及确保递归有基准情况。利用调试器、编写注释及查阅资料也有助于提高编程效率。避免这些错误可使代码更稳定、高效。
546 12
|
3月前
|
算法 Unix 数据安全/隐私保护
Python编程--UNIX口令破解机
Python编程--UNIX口令破解机
34 1
|
4月前
|
存储 算法 Linux
C语言 多进程编程(一)进程创建
本文详细介绍了Linux系统中的进程管理。首先,文章解释了进程的概念及其特点,强调了进程作为操作系统中独立可调度实体的重要性。文章还深入讲解了Linux下的进程管理,包括如何获取进程ID、进程地址空间、虚拟地址与物理地址的区别,以及进程状态管理和优先级设置等内容。此外,还介绍了常用进程管理命令如`ps`、`top`、`pstree`和`kill`的使用方法。最后,文章讨论了进程的创建、退出和等待机制,并展示了如何通过`fork()`、`exec`家族函数以及`wait()`和`waitpid()`函数来管理和控制进程。此外,还介绍了守护进程的创建方法。
C语言 多进程编程(一)进程创建
|
4月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
4月前
|
Linux C语言
C语言 多进程编程(四)定时器信号和子进程退出信号
本文详细介绍了Linux系统中的定时器信号及其相关函数。首先,文章解释了`SIGALRM`信号的作用及应用场景,包括计时器、超时重试和定时任务等。接着介绍了`alarm()`函数,展示了如何设置定时器以及其局限性。随后探讨了`setitimer()`函数,比较了它与`alarm()`的不同之处,包括定时器类型、精度和支持的定时器数量等方面。最后,文章讲解了子进程退出时如何利用`SIGCHLD`信号,提供了示例代码展示如何处理子进程退出信号,避免僵尸进程问题。
|
4月前
|
消息中间件 Unix Linux
C语言 多进程编程(五)消息队列
本文介绍了Linux系统中多进程通信之消息队列的使用方法。首先通过`ftok()`函数生成消息队列的唯一ID,然后使用`msgget()`创建消息队列,并通过`msgctl()`进行操作,如删除队列。接着,通过`msgsnd()`函数发送消息到消息队列,使用`msgrcv()`函数从队列中接收消息。文章提供了详细的函数原型、参数说明及示例代码,帮助读者理解和应用消息队列进行进程间通信。
|
4月前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
4月前
|
消息中间件 Unix Linux
C语言 多进程编程(二)管道
本文详细介绍了Linux下的进程间通信(IPC),重点讨论了管道通信机制。首先,文章概述了进程间通信的基本概念及重要性,并列举了几种常见的IPC方式。接着深入探讨了管道通信,包括无名管道(匿名管道)和有名管道(命名管道)。无名管道主要用于父子进程间的单向通信,有名管道则可用于任意进程间的通信。文中提供了丰富的示例代码,展示了如何使用`pipe()`和`mkfifo()`函数创建管道,并通过实例演示了如何利用管道进行进程间的消息传递。此外,还分析了管道的特点、优缺点以及如何通过`errno`判断管道是否存在,帮助读者更好地理解和应用管道通信技术。