如何处理僵死进程?

来源:互联网 发布:广西广电网络网上缴费 编辑:程序博客网 时间:2024/05/02 04:41

如何处理僵死进程?

       给进程设置僵尸状态的目的是维护子进程的信息,以便父进程在以后某个时间获取,这些信息包括进程的 pid、终止状态以及资源的利用信息(CPU时间,内存使用量等)。如果一个进程终止,而该进程有子进程处于僵尸状态,那么它的所有僵尸子进程的 ppid 将被重置为1(init 进程)。继承这些子进程的 init 进程将清理它们(init 进程将 wait 它们,从而去除僵死状态)。

       但在通常情况下,我们是不愿意存留僵死进程的,它们占用内存空间,最终可能导致我们耗尽进程资源,那么将如何避免产生僵死进程呢?

      (1)父进程调用 pid_t wait(int *status)函数来获取子进程的退出状态

               函数功能:父进程一旦调用了 wait 就立即阻塞自己,由 wait 自动分析是否当前的某个子进程已经退出,如果让它找到了一个已经变成僵死进程的子进程,wait 就会收集这个子进程的信息,并把它彻底摧毁后返回;如果没有找到这样一个子进程,wait 就会一直阻塞在这里,直到有一个出现为止。

               函数返回值:当wait() 与 fork() 配套出现时,如果在使用fork()之前调用了 wait(),wait() 的返回值则为-1,正常情况下返回子进程的 pid。

               参数 status 用来保存被收集退出时的一些状态,它是指向 int 类型的指针。如果我们对子进程是怎样死掉的毫不在意,只想把这个僵死进程消灭掉,

               我们就可以设定这个参数为 NULL,即 pid = wait(NULL);调用成功,就会返回被收集的在子进程的 pid;如果调用的进程没有子进程,调用失败,返回-1。


      通过以上了解,我们可以了解到:辛辛苦苦创建了一个子进程,最终子进程和父进程成了串行运行,实际上并没有达到提高效率的目的。

      (2)异步处理僵死进程 ----> 我们提出了信号的概念

               信号的概念:系统先定义好的某些特定的事件,可以被发生,也可以被接收,发生和接收的主体都是进程。

               系统中信号的定义:/usr/include/bits/signal.h

              信号的来源:按照产生条件的不同可以分为软件和硬件两种

                      a. 硬件方式:当用户在终端上按下某键时,将产生信号;如果按下组合键((Ctrl + c 代表1)SIGINT 中断信号;(Ctrl + \代表是 3)SIGQUIT 退出信号;(Ctrl + z 代表19)SIGSTOP 信号)后将产生一个信号

                      b. 软件方式:用户在终端下调用 kill 命令向进程发送任务信息

              信号的种类:在 shell 下输入 kill -l 可显示linux系统支持的全部信号;信号的值定义在 signal.h 中。每个信号都由一个编号和宏定义名称

              信号的接受:在接受信号的进程 PCB 结构中有 long signal,通过 signal 来表示信号

              进程对信号的响应:当进程发生时,用户可以要求进程以以下三种方式之一对信号做出响应:

                     a. 默认信号:按系统默认方式处理,大部分信号的默认操作是终止操作,且所有的实时信号的默认动作都是终止进程

                     b. 忽略信号:大多数信号都可以使用这种方式进行处理,但是SIGKILL和SIGSTOP这两个信号不能忽略,同时这两种信号也不能捕获和阻塞。此外,如果会忽略某些由硬件异常产生的信号(如非法存储访问或除以0),则进程的行为是不可预测的。

                     c. 捕捉信号:对于捕捉的信号,可以为其指定信号处理函数,信号发生时该函数自动被调用,在该函数内部实现对信号的处理。

              修改信号的响应方式:修改 PCB 结构中,struct sigaction 结构体数组中对应信号值作为下标的函数指针

                     函数原型:void (*signal(int signum, void (*fun))(int))(int);//signum 指定信号的值,int 指函数指针对前面信号值的处理;

                     帮助理解:typedef void (*fun_handle)(int);

                                      fun_handle signal(int signum, fun_handler fun);

                     返回值:调用成功返回最后一次安装信号 signum 而调用 signal() 时的 fun 值,失败则返回 SIG_ERR。

              修改信号的响应方式的时机:进程刚开始就执行修改关注的信号的响应方式。

              代码中 signal 在何时调用:一般情况下在 main 函数开始第一行调用。

              结合信号来处理僵死进程,父进程不阻塞并且处理僵死进程,那子进程结束时,只需要向其父进程发送一个 SIGCHID 信号。

              优势:a. 父进程和子进程可以并行处理;

                        b. 可以处理所有的僵死进程。

示例如下:

#include <stdio.h>#include <unistd.h>#include <assert.h>#include <signal.h>void fun(int sign){printf("fun was called\n");pid_t pid = wait(NULL);printf("pid == %d\n", pid);printf("fun over\n");}void main(){pid_t pid = fork();assert(pid != -1);if (pid == 0){printf("child start\n");sleep(2);printf("child end\n");kill(getppid(), SIGINT);//子进程结束时会向其父进程发送SIGCHLD}else{signal(SIGCHLD, fun);printf("father start\n");sleep(10000);printf("father end\n");}}

运行结果如下:




还没完呢。。。。。。。


              



原创粉丝点击