首页 文章

终端与所有孩子一起失败,即使是SIGKILLed,但正常的过程不会这样做

提问于
浏览
0

我希望在我的程序中具有与bash(终端)在使用SIGKILL杀死它时所做的相同的效果 . 我们知道我们无法在我们的程序中处理SIGKILL,所以当我杀死我的程序时,它的子程序被分配给init进程,没有办法处理它,这样我就可以杀死所有子进程然后杀死父进程本身 . 虽然我们杀死终端时,即使我们通过SIGKILL杀死我们的终端,所有通过它创建的进程也会被杀死 .

为此,我做了一些研究,发现了以下帖子:[https://unix.stackexchange.com/questions/54963/how-can-terminal-emulators-kill-their-children-after-recieving-a-sigkill][1]

这个帖子有点令人困惑,但我发布的帖子仍然是,如果您要杀的进程是进程组的进程负责人,那么它的所有子进程都会被杀死 .

所以为了简单起见,我在下面的程序中实现了测试是否如此:

int main()
{
    printf("Curent PID: %u\n", getpid());

    // make a new session
    pid_t pid = setsid();

    printf("New session ID: %u\n", pid);

    pid = fork();
    switch(pid)
    {
            case -1:
                    perror("UNable to fork the process\n");
                    exit(EXIT_FAILURE);
            case 0:
                    // child process
                    while (1)
                    {
                            sleep(1);
                    }
                    break;
    }

    while (1)
    {
            printf("Process Leader running\n");
            sleep(1);
    }

return 0;

}

在我杀死父进程后运行上述程序后,子进程没有被杀死 . 我还修改了上面的程序,使它不属于任何tty,我想可能是进程领导者不应该与任何tty相关联 . 我通过以下方式做到了这一点:

创建正常流程(父流程)

从上述父进程中创建子进程

此阶段的流程层次结构如下:TERMINAL - > PARENT PROCESS - > CHILD PROCESS

终止父进程 .

子进程现在变成了孤儿,并被init进程接管 .

调用setsid()函数在新会话中运行该进程并拥有一个新组 . 然后相同的上面的代码重复 .

仍然当我杀死进程领导者时,孩子们在那里 . 也许我没有在unix.stackexchange上发帖,或者它是LINUX中的嘲讽行为 . 我可以通过捕获每个TERMINATING SIGNAL(如SIGTERM,SIGHUP等)实现的一种方法是处理它们并在这些信号处理程序中编写逻辑以首先杀死孩子 . 但仍然在SIGKILL上我无能为力 .

此外,我有兴趣知道,如果父进程不会影响子进程,即使父进程是进程领导者还是其他什么,那么即使我们向其发送SIGKILL,bash(终端)如何设法杀死所有子进程 . 是否为LINUX内核中的终端编写了一些额外的逻辑 .

如果有一种方法可以杀死所有子进程,即使使用SIGKILL信号也可以杀死父进程,我也很高兴知道这一点 .

3 回答

  • 1

    杀手册页说:

    负PID值可用于选择整个过程组

    在我理解杀死整组进程时,你必须发送负PID .

    另一种机制导致杀死终端杀死其子进程 . 从终端运行的进程将stdin / stdout连接到终端 . 终止终端时,关闭这些连接并向这些进程发送信号(SIG_HUP) . 通常的程序不处理此信号,默认终止 .

  • 1

    玛丽安的建议是非常正确的,非常值得研究,但如果你选择遵循这条路线,你可能会最终实现所谓的“人质技巧” .

    人质技巧包括产生人工子进程的根进程,该进程将所有时间都停留在停止状态 . 这个“人质”将在第一个子进程之前立即生成,该进程在您的(多进程)程序中真正起作用 .

    人质过程成为其自己的进程组的领导者,然后进入一个循环,在该循环中,它通过“raise(SIGSTOP)”停止 . 如果它继续,它检查它的父母是否已经终止(即它是否已被重新父母或不能用空信号(ESRCH)向其父母发信号) . 如果父母已经终止,那么人质应该终止,否则它应该用另一个“加注(SIGSTOP)”重新暂停 .

    你需要注意竞争条件:例如:对于重新父母测试,请注意将hostage的parent-process-id缓存为“fork()”之前的“getpid()”的返回值 - 使用该人质,并使“setpgid()”调用下游父母和孩子都有“fork()” . 如果有人“杀死( . ,SIGKILL)”作为人质,你需要考虑你做了什么!

    确实,您可以在父级中放置一个SIGCHLD处理程序来重新生成它,但这需要非常小心以保持人质进程组身份的连续性;也许在SIGKILL时有其他子进程,替换人质应该进入原始进程组,可能没有,原始进程组已经蒸发 .

    即使你做对了,如果你的主进程使用多个线程,你在异步信号(SIGCHLD)的处理程序中放入“fork()”调用的事实可能会打开另一个蠕虫 .

    由于这些困难,我建议不要使用人质技巧,除非子进程运行你无法控制的代码(并认真思考)关于复杂性和可维护性的成本,即使那时) . 如果您可以控制子进程的代码,那么使用“pipe()”就会简单得多 .

    您在父进程中创建管道并管理文件描述符以确保父进程是唯一的编写器,并且每个子进程都将一个文件描述符分配给读取端 . 如果执行此操作,则在最后一个写入程序终止时,父进程的终止(无论是由于SIGKILL还是由于任何其他原因)通过管道读取端的EoF条件传递给子进程 .

    如果你想特别对待SIGKILL,那么你可以在管道上使用一个协议,父进程发送一个终止消息,告知孩子在所有正常终端上的终止状态和可捕获的致命信号,并让孩子们推断出如果管道的读取端在没有先前终止消息的情况下发送EoF,则SIGKILL将查找父节点 .

  • 0

    在Linux上 prctl(PR_SET_PDEATHSIG ...将安排一个进程在父进程死亡时接收信号,此设置将保留在 exec 上,但不会被子进程继承 .

相关问题