IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux-僵尸进程产生与处理 -> 正文阅读

[系统运维]Linux-僵尸进程产生与处理

目录

背景

产生

处理方法?

方法一:父进程通过wait或者wait_pid方式回收子进程

方法二:信号处理signal

改进版


背景

????????父进程创建子进程之后,父进程没有等待该子进程的退出,子进程就会成为僵尸进程,如果父进程也退出,这个时候子进程也可以被init进程回收,释放资源。如果父进程不退出,子进程占用的资源将永远不会被释放。

产生

以下示例是一个产生僵尸进程的典型例子

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


int main(int argc, char **argv) {

    pid_t pid = fork();

    if(pid > 0){
        printf("i am parent process PID=%d\n", getpid());
        sleep(10);
    }else if(pid == 0) {
        printf("i am child process PID=%d\n", getpid());
        return 0;
    }else{
        printf("fork error\n");
        return -1;
    }
    return 0;

}	

运行结果:

$ ./test
i am parent process PID=1742
i am child process PID=1743

查看进程:

$ ps -ef | grep test
pi 1742 1040 0 19:19 pts/0 00:00:00 ./test
pi 1743 1742 0 19:19 pts/0 00:00:00 [test] <defunct>
pi 1746 1040 0 19:19 pts/0 00:00:00 grep --color=auto test

当主进程十秒运行结束之后

[1]+ 已完成 ./test
$ ps -ef | grep test
pi 1750 1040 0 19:19 pts/0 00:00:00 grep --color=auto test

????????可以看到有“已完成”字样的打印,但是再次查看进程列表的时候发现,僵尸进程[test] <default>已经不存在了,从以上的打印可以看到,开始主进程运行进行sleep等待,子进程运行结束之后立马退出,产生僵尸进程;等到父进程1742结束之后,僵尸进程1743被init进程回收。

处理方法?

????????那么如何消除这种子进程退出后成为僵尸进程的问题呢?

方法一:父进程通过wait或者wait_pid方式回收子进程

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


int main(int argc, char **argv) {

    pid_t pid = fork();
    int status, i;

    if(pid > 0){
        printf("i am parent process PID=%d\n", getpid());
        //wait(NULL);
        wait(&status);
        i = WEXITSTATUS(status);
        printf("child process done! status=%d\n", i);
       //sleep(10);
    }else if(pid == 0) {
        printf("i am child process PID=%d\n", getpid());
        sleep(5);
        return 0;
    }else{
        printf("fork error\n");
        return -1;
    }
    return 0;

}

运行结果

i am parent process PID=1841
i am child process PID=1842
child process done! status=0

????????可以看到产生子进程之后打印 printf("child process done!\n");被wait阻塞,说明wait是阻塞型的,同样我们通过ps -ef查看进程列表,可以发现没有出现僵尸进程。当我们不关心子进程状态时候,这里wait的参数可以设定为NULL。

????????watipid(pid_t pid, int *status, int options)是在wait(int *status)的基础上增加了一些个性化设定,可以监听一组、或者指定的子进程pid,具体使用如下:

参数pid
pid<-1 等待进程组识别码为pid绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程。

参数options
参数option可以为0 或下面的OR 组合
WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以等待。
WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。

参数status返回值
WIFEXITED(status)如果子进程正常结束则为非0值。
WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真
WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。
WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。

waitpid返回值
如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno中

????????上面这个会有一个问题,调用wait的父进程会被一直阻塞,它无法继续执行后面的任务,直到子进程退出之后。那么针对这种情况如何处理呢?可以将wait用信号来代替

方法二:信号处理signal

void (*signal(int signum,void(* handler)(int)))(int);

函数说明:
signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。如果参数handler不是函数指针,则必须是下列两个常数之一:
SIG_IGN 忽略参数signum指定的信号。
SIG_DFL 将参数signum 指定的信号重设为核心预设的信号处理方式。

????????子进程退出的时候内核会发送SIGCHLD给父进程,所以父进程可以监听这个信号,并设定信号处理函数,上面的示例可以改进如下:

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

void childProcess(){
    int round = 10;

    while(round > 0){
        printf("round=%d\n", round);
        sleep(1);
        round--;
    }
}

void sig_handler(int signum){
    pid_t pid;
    if(signum == SIGCHLD){
        printf("get child exit signal\n");
        while((pid = waitpid(-1, NULL, WNOHANG)) > 0){
            printf("child %d exit\n", pid);
        }
    }
}

int main(int argc, char **argv) {

    pid_t pid = fork();
    //int status, i;
    signal(SIGCHLD, sig_handler);
    int tmp = 10;

    if(pid > 0){
        printf("i am parent process PID=%d\n", getpid());
        //wait(NULL);
        //wait(&status);
        //i = WEXITSTATUS(status);
        //printf("child process done! status=%d\n", i);
        sleep(5);
        printf("do parent`s thing\n");
        while(tmp > 0){
            sleep(1);
            printf("parent loop =%d\n", tmp);
            tmp--;
        }
        printf("parent exit\n");
        return 0;
    }else if(pid == 0) {
        printf("i am child process PID=%d\n", getpid());
        //sleep(5);
        childProcess();
        return 0;
    }else{
        printf("fork error\n");
        return -1;
    }
    return 0;

}

运行效果

./test
i am parent process PID=2014
i am child process PID=2015
round=10
round=9
round=8
round=7
round=6
do parent`s thing
round=5
parent loop =10
round=4
parent loop =9
round=3
parent loop =8
round=2
parent loop =7
round=1
parent loop =6
get child exit signal
child 2015 exit
parent loop =5
parent loop =4
parent loop =3
parent loop =2
parent loop =1
parent exit

????????这里需要注意,我们用的是waitpid方式来处理子进程回收的监听,如果系统繁忙时,有两个子进程同时结束,这时只会发送一个SIGCHLD信号,如果只wait一次,也会产生僵尸进程,所以这里使用waitpid加带入WNOHANG参数,使得调用的时候立马返回,根据返回值判断等待结果。

改进版

当然比较简单的应用,我们可以直接用wait(NULL)的方式,它等价于waitpid(-1, NULL, 0),具体可以看上面关于waitpid参数说明。

当然我们也可以给signal的第二个参数直接输入一个常数,也可以达到回收效果

signal(SIGCHLD, SIG_IGN);

以上表示直接忽略SIGCHLD信号,让父进程不必关心子进程退出状态,直接清理。

如果参数handler不是函数指针,则必须是下列两个常数之一:
SIG_IGN 忽略参数signum指定的信号。
SIG_DFL 将参数signum 指定的信号重设为核心预设的信号处理方式。

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

void childProcess(){
    int round = 10;

    while(round > 0){
        printf("round=%d\n", round);
        sleep(1);
        round--;
    }
}

void sig_handler(int signum){
    pid_t pid;
    if(signum == SIGCHLD){
        printf("get child exit signal\n");
        while((pid = waitpid(-1, NULL, WNOHANG)) > 0){
            printf("child %d exit\n", pid);
        }
    }
}

int main(int argc, char **argv) {

    pid_t pid = fork();
    //int status, i;
    //signal(SIGCHLD, sig_handler);
    signal(SIGCHLD, SIG_IGN);
    int tmp = 10;

    if(pid > 0){
        printf("i am parent process PID=%d\n", getpid());
        //wait(NULL);
        //wait(&status);
        //i = WEXITSTATUS(status);
        //printf("child process done! status=%d\n", i);
        sleep(5);
        printf("do parent`s thing\n");
        while(tmp > 0){
            sleep(1);
            printf("parent loop =%d\n", tmp);
            tmp--;
        }
        printf("parent exit\n");
        return 0;
    }else if(pid == 0) {
        printf("i am child process PID=%d\n", getpid());
        //sleep(5);
        childProcess();
        return 0;
    }else{
        printf("fork error\n");
        return -1;
    }
    return 0;

}

i am parent process PID=2047
i am child process PID=2048
round=10
round=9
round=8
round=7
round=6
do parent`s thing
round=5
parent loop =10
round=4
parent loop =9
round=3
parent loop =8
round=2
parent loop =7
round=1
parent loop =6
parent loop =5
parent loop =4
parent loop =3
parent loop =2
parent loop =1
parent exit

从打印看,子进程退出之后,父进程并没有做特殊处理,直接回收(可以在子进程退出执行完之后,ps查看是否有对应子进程pid)。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-09-06 11:30:56  更:2021-09-06 11:33:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 13:39:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码