僵尸进程的产生和预防

进程变为僵尸进程的过程

当一个子进程结束时,它会向父进程发送一个SIGCHLD信号,并进入一个终止状态,等待父进程来读取它的退出状态。这个终止状态就是我们所说的“僵尸状态”,而处于这种状态的进程就是“僵尸进程”。

具体过程如下:

  1. 子进程终止:子进程执行完毕,调用 exit() 函数或返回 main() 函数。
  2. 发送信号:子进程终止后,会向父进程发送一个 SIGCHLD 信号,通知父进程它已经结束。
  3. 进入僵尸状态:子进程的进程控制块(PCB)仍保存在系统中,保存其退出状态和其他资源,等待父进程读取。此时子进程变成僵尸进程。
  4. 父进程处理:父进程需要调用 wait()waitpid() 函数读取子进程的退出状态,这样子进程的所有资源才会被释放,僵尸进程才会完全消失。

如果父进程没有及时调用 wait()waitpid(),僵尸进程会一直存在,占用系统资源。

预防措施

为避免僵尸进程占用系统资源,可以采取以下措施:

1. 使用 wait()waitpid()

父进程可以调用 wait()waitpid() 函数等待子进程结束,并读取其退出状态。这是最常见的处理方式。

#include <sys/wait.h>

// 在父进程中等待子进程结束
int status;
pid_t pid = wait(&status);

wait会导致父进程阻塞

2. 处理 SIGCHLD 信号

父进程忙,可以设置信号处理函数来处理 SIGCHLD 信号,在信号处理函数中调用 waitpid() 来清理子进程资源。

#include <signal.h>
#include <sys/wait.h>

void sigchld_handler(int sig)
{
    // 等待任何子进程结束,避免僵尸进程
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main()
{
    struct sigaction sa;
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;

    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    // 父进程的其他代码

    return 0;
}
3. 使用 SA_NOCLDWAIT 标志

在某些系统中,可以使用 sigaction 函数设置 SA_NOCLDWAIT 标志,这样子进程结束后不需要显式调用 wait()waitpid(),内核会自动清理子进程的资源。

struct sigaction sa;
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDWAIT;

if (sigaction(SIGCHLD, &sa, NULL) == -1) {
    perror("sigaction");
    exit(1);
}
4. 使用 prctl(PR_SET_CHILD_SUBREAPER)

在 Linux 系统中,可以使用 prctl 函数设置一个进程为“子进程重组器”,该进程的所有子进程结束时,如果其父进程已经退出,则该进程会负责清理它们的资源。

#include <sys/prctl.h>

int main()
{
    if (prctl(PR_SET_CHILD_SUBREAPER, 1) == -1) {
        perror("prctl");
        exit(1);
    }

    // 父进程的其他代码

    return 0;
}

参考书

函数说明见《TCP/IP网络编程》第10章内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值