Linux信号:SIGCHLD信号和僵尸进程

1. SIGCHLD信号产生条件:

        (1)子进程终止;

        (2)子进程收到SIGSTOP信号被暂停;

        (3)子进程处于暂停状态,收到SIGCONT信号被唤醒。


2. 捕捉SIGCHLD,避免僵尸进程:

方式1:

注册信号处理函数,捕捉SIGCHLD信号后回收。

示例:

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

void waitforchild(int signum) {  // 收到子进程退出信号,才会执行信号处理函数
    printf("捕捉到信号%d, 回收子进程..\n", signum);
    while (waitpid(-1, NULL, WNOHANG) > 0) { // 非阻塞方式回收子进程
    }
    printf("回收到一个子进程,退出回收处理函数...\n");
}

int main(int argc, const char* argv[]) {

    /*
    /*注册回收子进程信号处理函数前,需要将子进程退出的信号加入阻塞信号集,
      防止注册信号处理函数前,有子进程退出*/
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set,SIGCHLD); // 将SIGCHLD加入信号集set
    sigprocmask(SIG_BLOCK, &set, NULL); // 将信号集set加入阻塞信号集
    */ // 这段代码在这里不是必须的,因为fork之前已经注册了信号处理函数

    pid_t pid = -1;
    int ret = -1;

    /*注册子进程退出信号处理函数*/
    struct sigaction act;
    act.sa_handler = waitforchild; // 信号处理函数
    act.sa_flags = 0; // 使用旧的信号处理函数
    sigemptyset(&act.sa_mask); // 确保执行信号处理函数时,不会被其他信号打断
    // 收到子进程退出信号,才会执行信号处理函数
    ret = sigaction(SIGCHLD, &act, NULL);
    if (-1 == ret) {
        perror("sigaction");
        return 1;
    }
    sigprocmask(SIG_UNBLOCK, &set, NULL); // 将set信号集从阻塞信号集中移除

    pid = fork();
    if (-1 == pid) {
        perror("fork");
        return 1;
    }

    if (0 == pid) { // 子进程
        sleep(1);
        printf("子进程即将退出!\n");
        exit(0);
    } else { // 父进程
        sleep(2);
        system("ps -aux | grep Z"); // 查看是否有僵尸进程
    }

    printf("父进程也结束了.\n");
    return 0;
}

运行结果:

根据结果中橙色方框的内容:回收了两次子进程,说明存在两个子进程;但程序中只有一个fork,为什么会有两个子进程呢?

注意:43行的system也会创建一个子进程!


方式2:

若父进程不关心子进程结束,则可使用sigaction或signal函数通知内核父进程会忽略SIGCHLD信号,则子进程结束后,内核会回收,并不再给父进程发送该信号。

示例:

本程序中,子进程先退出了,且父进程未使用wait或waipid回收。

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

int main(int argc, const char* argv[]) {

    pid_t pid = -1;
    int ret = -1;

    struct sigaction act;
    act.sa_handler = SIG_IGN; // 忽略该信号
    act.sa_flags = 0; // 使用旧的信号处理函数

    /*忽略SIGCHLD信号,
    则子进程结束后,由内核回收,不会再给父进程发信号*/
    ret = sigaction(SIGCHLD, &act, NULL);
    if (-1 == ret) {
        perror("sigaction");
        return 1;
    }

    pid = fork();
    if (-1 == pid) {
        perror("fork");
        return 1;
    }

    if (0 == pid) { // 子进程
        sleep(1);
        printf("子进程已结束!\n");
        exit(0);
    } else { // 父进程
        sleep(2);
        system("ps -aux | grep Z"); // 查看是否有僵尸进程
    }

    printf("父进程也结束了.\n");
    return 0;
}

运行结果:

ps命令看到无僵尸进程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux 中,僵尸进程和孤儿进程都是指与父进程不再有联系的进程,它们通常是由于进程管理不当或程序错误导致的。 **僵尸进程**是已经完成执行任务,但其父进程还没有来得及处理其退出状态的进程。当进程完成执行后,它的退出状态(也称为退出码或终止状态)会被保存在系统中,直到父进程通过 `wait` 或 `waitpid` 等函数来获取该状态。如果父进程没有处理该状态,那么该进程就会成为僵尸进程,占用系统资源。要清理僵尸进程,可以使用 `kill` 命令向其父进程发送 `SIGCHLD` 信号,或者重新编写程序,使其正确处理子进程的退出状态。 **孤儿进程**是指其父进程已经退出或被终止,但其自身仍在运行的进程。孤儿进程会被 `init` 进程进程号为 `1`)接管,`init` 进程会定期检查系统中是否有孤儿进程,并且将其的父进程设置为 `init` 进程。要避免孤儿进程的产生,可以在父进程退出之前,等待子进程的退出,或者将子进程的父进程设置为 `init` 进程。 可以使用 `ps` 命令来查看系统中的僵尸进程和孤儿进程。使用以下命令可以查看所有僵尸进程: ``` ps aux | grep 'Z' ``` 其中,`aux` 选项用于显示所有进程,`grep 'Z'` 用于查找所有状态为 `Z` 的进程,即僵尸进程。 使用以下命令可以查看所有孤儿进程: ``` ps -ejH ``` 其中,`-e` 选项用于显示所有进程,`-j` 选项用于以层次结构的形式显示进程,`-H` 选项用于显示所有孤儿进程

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的马师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值