【问题标题】:Child process should not die with parent process子进程不应与父进程一起死亡
【发布时间】:2022-01-20 14:46:38
【问题描述】:

以下场景:

  • process_1.bin 开始 doSomeThing.sh

  • doSomeThing.sh 应该先杀死process_1.bin,然后继续自己运行并做一些其他事情

我尝试过 fork、exec、screen、p_threads、daemon,还有一些没有成功。 每次doSomeThing.sh启动并杀死process_1.bin,它都会杀死自己,因为父进程被杀死。

doSomeThing.sh 告诉我:“kill 信号后退出程序!”

很遗憾,这也不起作用:

        pid_t pid;
        pid = fork();
        if (pid < 0)
            exit(EXIT_FAILURE);

        if (pid > 0)            // Success: Let the parent terminate
            exit(EXIT_SUCCESS);

        if (setsid() < 0)
            exit(EXIT_FAILURE);

        pid = fork();
        if (pid < 0)
            exit(EXIT_FAILURE);

        if (pid > 0)
            exit(EXIT_SUCCESS);
    
    //     umask(0);  

    // chdir("/");

        switch(pid)
        {
        case -1: // Fork() has failed
            perror ("fork");
            break;
        case 0: // This is processed by the child
// deamon(0,0);            
          system("/root/doSomeThing.sh");
// system("screen -dmS doSomeThing /root/doSomething.sh")

//     char *name[] = {
//    "/bin/bash",
//    "-c",
//    "/root/doSomeThing.sh",
//    NULL
//    };
//     if(execvp(name[0], name) < 0)
//       perror("execvp");

            exit(0);
            break;
        default:    // This is processed by the parrent
            break;
        }

我怎样才能指挥doSomeThing.sh 杀死process_1.bin 但仍然保持自己的生命?

【问题讨论】:

  • 如果你想很好地控制信号,你可能不能使用system。分叉您自己的进程来执行脚本(例如,通过exec("/bin/sh" .... 或类似方法)并控制信号处理。
  • 我已经试过了:``` pid_t pid; pid = fork();如果(pid 0) // 成功:让父节点终止 exit(EXIT_SUCCESS);如果 (setsid() 0)退出(EXIT_SUCCESS); char *name[] = { "/bin/bash", "-c", "/root/doSomeThing.sh", NULL }; switch(pid) { ... case 0: execvp(name[0], name) break; ... } ```
  • 你能在doSomething.sh中显示代码吗?子进程杀死其父进程并不是一个特别好的主意(杀父和杀母在大多数圈子中都是不受欢迎的)。 process1.bin 是你展示一些零碎代码的 C 程序吗? doSomething.sh 是一个 shell 脚本吗?它如何确定其父进程的PID?
  • @ikegami — 在编程圈子里,父母的过程有时确实需要杀婴;令人沮丧的是,父母的过程常常不得不等待他们的孩子死去。多进程编程是一门病态的生意!
  • 如果您对fork() 的调用返回一个正值(子进程的pid),则您已经退出当前进程(即父进程)。为什么还要再杀一次?如果您的子进程不执行脚本而是执行其他操作(睡眠 300,找到 10 亿个素数),会发生什么?它会死吗?

标签: c linux process parent kill


【解决方案1】:

除了 Jonathan Leffler 建议的忽略 SIGTERM 之外,您还可以将子进程设置为 守护进程,这样它的生命将独立于其父进程的生命。 Linux 有一个命令来启动一个进程作为守护进程daemonize 或一个库函数,用于愿意与其父进程和控制终端daemon 分离的程序。

【讨论】:

  • 如果您想直接在 C 中执行此操作:Creating a daemon in Linux 答案中列出的步骤通常适用于基于 POSIX 的系统,而不仅仅是 Linux。
【解决方案2】:

您的子进程不应死亡。这是一对模拟您描述的shell脚本。

process1.bin

#!/bin/sh

echo "$0: at work"
trap 'echo "$0: Death threat received"; exit 1' 1 2 3 13 15

sh doSomething.sh &

echo "$0: so far, so good"
wait
echo "$0: it's sad when all your children die"
ps -f
echo "$0: exiting normally"
exit 0

doSomething.sh

#!/bin/sh
#
# Commit parenticide and continue, preferably without dying.

#PPID=$(ps -fp$$ | colnum -c 3)

echo "Before"
ps -f

(
set -x
kill $PPID
)

sleep 1

echo ""
echo "After"
ps -f

sleep 5
echo "$0: Goodbye, Cruel World!"

有一次我运行它,我得到了输出:

$ ./process1.bin
./process1.bin: at work
./process1.bin: so far, so good
Before
  UID   PID  PPID   C STIME   TTY           TIME CMD
  501   649   641   0  9:58AM ttys000    0:00.14 -bash
  501 29760   649   0  8:47AM ttys000    0:00.01 sh
  501 29793 29760   0  8:47AM ttys000    0:00.01 /bin/sh ./process1.bin
  501 29794 29793   0  8:47AM ttys000    0:00.01 sh doSomething.sh
  501   661   657   0  9:58AM ttys001    0:00.05 -bash
  501   693   688   0  9:58AM ttys002    0:00.05 -bash
  501   738   731   0  9:58AM ttys007    0:00.31 -bash
  501   769   766   0  9:58AM ttys008    0:00.05 -bash
  501   848   847   0  9:58AM ttys009    0:00.05 -bash
  501   884   881   0  9:58AM ttys010    0:00.12 -bash
  501   946   920   0  9:58AM ttys011    0:00.15 -bash
+ kill 29793
./process1.bin: Death threat received
$ 
After
  UID   PID  PPID   C STIME   TTY           TIME CMD
  501   649   641   0  9:58AM ttys000    0:00.14 -bash
  501 29760   649   0  8:47AM ttys000    0:00.01 sh
  501 29794     1   0  8:47AM ttys000    0:00.01 sh doSomething.sh
  501   661   657   0  9:58AM ttys001    0:00.05 -bash
  501   693   688   0  9:58AM ttys002    0:00.05 -bash
  501   738   731   0  9:58AM ttys007    0:00.31 -bash
  501   769   766   0  9:58AM ttys008    0:00.05 -bash
  501   848   847   0  9:58AM ttys009    0:00.05 -bash
  501   884   881   0  9:58AM ttys010    0:00.12 -bash
  501   946   920   0  9:58AM ttys011    0:00.15 -bash
doSomething.sh: Goodbye, Cruel World!

$

请注意,我的$ 提示出现在“收到死亡威胁”之后。以下输出来自doSomething;我在“再见,残酷的世界!”之后点击返回。得到最终的$ 提示。

在运行 macOS Catalina 10.15.7 的 MacBook Pro 上使用 Bash 进行测试。


commentoemer_1907 中说:

shell 脚本杀死所有进程以播放更新(包括 C 更新进程)。一旦 C 更新进程被 shell 更新进程杀死,它就会向它的所有子进程发送一个 SIGTERM。因此,shell 脚本也被杀死。相反,如果它只是计算而不杀死它的父级,那么它会一直运行到最后。

如果父进程向其所有子进程发送 SIGTERM,则您的子脚本 doSomething.sh 必须使用以下任一方法忽略 SIGTERM 信号:

trap "" 15
trap "" TERM

在它向其父级发送信号之前。或者父进程可以安排不向该子进程发送信号。或者它可以安排孩子在执行之前忽略 SIGTERM:

signal(SIGTERM, SIG_IGN);
…exec child…

或者……

【讨论】:

  • 非常感谢。但是,我想使用 C 语言在顶层(Process_1)上解决它。是否可以在 process_1 页面上停止发送 doSomeThing.sh 上的信号(例如信号 15)?
  • 所以,在使用execvp()之前,在孩子中调用signal(SIGTERM, SIG_IGN);(必要时,在fork之后)。
  • 顺便提一下,由于您没有提供 MCVE(Minimal, Complete, Verifiable Example — 或 MRE 或 SO 现在使用的任何名称)或 SSCCE(Short, Self-Contained, Correct Example — 相同的想法,但名称不同) ,我无法修改你的 C 代码。请不要提供代码片段;提供 MCVE。
  • 非常感谢。现在问题已经解决了。带有 system() 的 fork() 效果很好。我的错误是我将两个进程命名为相同。 -update.bin -update.sh 杀死进程时它不是在寻找 PPID,而是在寻找名为“update”的进程。所以 update.sh 杀死了自己(update.bin 没有终止信号)。它现在可以工作了 - 我将它重命名为: - updater.bin - update.sh 非常感谢您提供快速、友好和信息丰富的帮助。
猜你喜欢
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
  • 2018-02-22
  • 2014-06-19
  • 1970-01-01
  • 2014-09-16
  • 1970-01-01
相关资源
最近更新 更多