Bash 使用 && 而不是 ; 创建子进程对于 nohup 程序

Bash 使用 && 而不是 ; 创建子进程对于 nohup 程序
  • 我有一台 MacBook(默认 shell 是 zsh)

  • 我有一个可执行的 python 脚本(脚本1.py)。

  • 我使用另一个可执行脚本(称为起动机)运行脚本1.py 当我的计算机启动时,运行的终端窗口起动机自动打开和关闭,并且脚本1.py进程仍然作为单独的进程运行。 (起动机当我的计算机打开时,通过 szh shell 实例自动运行)。原本的起动机脚本文件如下:

#!/bin/bash

cd /script1; nohup ./script1.py &

这个脚本运行正常,一切都很好。

但是,我试图了解当我在脚本中使用 (&&) 而不是 (;) 时会发生什么。 IE

#!/bin/bash

cd /scritp1 && nohup ./script1.py &

我的问题是,这也有效,但每当我尝试终止 python 脚本的进程时,我注意到 szh 或其他东西(可能是 bash 进程)的终端实例正在作为进程运行。即如果我打电话附注在终端我得到

ps -ef | grep "script1" 
123 14679     1   0  2:12PM ??       0:00.00 /bin/bash /script1 starter
123 14680 14679   0  2:12PM ??       0:03.46 /PythonFolder/python ./script1.py
123 14690 14683   0  2:12PM ttys000  0:00.00 grep scrip1

对于脚本起动机使用&&

ps -ef | grep "script1" 
123 14644     1   0  2:08PM ??       0:03.46 /PythonFolder/python ./script1.py
123 14652 14647   0  2:08PM ttys000  0:00.00 grep scrip1

为了起动机脚本使用;

为什么我在一个版本中得到两个进程,而在另一个版本中只有一个进程?我试图理解为什么运行的 szh起动机当我使用时创建一个运行我的 python 脚本的子进程&&为什么当我的入门脚本使用时它会终止;并使 python 脚本作为单个进程运行。

当我跑步时起动机与使用的脚本&&终端应用程序出现并关闭,但留下两个正在运行的进程(1467914680在这种情况下)。

如果我使用起动机脚本使用;我想终止我的 python.py 进程,我只需调用

kill 14644

但如果我使用起动机使用的脚本&&我想杀死两个进程我注意到杀死子进程或杀死父进程然后子进程起作用,即

kill 14680

或者

kill 14679; kill 14680

我还注意到我可以杀死父进程

kill 14679

我的 python 脚本将继续照常运行。

答案1

关键在于如何命令列表工作(此处引用 Bash 手册,因为您使用的#!/bin/bash是 shebang 行):

列表是由运算符 ';'、'&'、'&&' 或 '||' 之一分隔的一个或多个管道的序列,并且可以选择以 ';'、'&' 或新队。

在这些列表运算符中,&& 和 ||具有同等优先级,后跟 ;和 &,具有相同的优先级。

(还有一个管道|是由或)分隔的一个或多个命令的序列|&

这意味着在

cd /script1; nohup ./script1.py &

由于运算符之间的优先规则,shell 会看到cd /script1由 终止的list ;,然后在“主”shell 中同步运行,以及nohup ./script1.py由 终止的list &,然后在生成的单独进程中异步运行。

另一方面,在

cd /scritp1 && nohup ./script1.py &

shell 看到 AND 列表cd /scritp1 && nohup ./script1.py,以 终止&,并异步运行整个列表。这需要生成一个新bash进程来执行列表本身(在后台),这反过来又为您的 Python 脚本生成一个单独的进程。

关于kill:杀死父进程不会自动终止其子进程。这是标准的 shell 行为。在您的列表的情况下&&,杀死子进程允许其父进程终止,因为父进程所做的就是等待子进程返回。

如果你想nohup ./script1.py根据成功有条件地执行cd /script1 避免为 AND 列表生成外壳,您可以将第二个元素包含在大括号:

cd /scritp1 && { nohup ./script1.py & }

或使用条件块:

if
  cd /scritp1
then
  nohup ./script1.py &
fi

答案2

我找到了一种创建不依赖于父进程的进程的简单方法

(<command here> &)

这会产生一个新的 shell,而新的 shell 又会产生另一个运行该命令的后台 shell。关闭运行上述命令的主 shell 不会停止该进程。运行该命令的进程的 PPID (init) 为 1。

发生这种情况是因为第二个 shell 是第三个 shell 的父进程,它完成执行而无需等待其子进程完成(它唯一的工作是生成第三个 shell)。然后孩子就成为孤儿并由 init 处理。

如上所述这里

进程可以使用 _exit 系统调用退出,这将释放进程用于重新分配的资源。因此,当进程准备终止时,它会通过称为终止状态的信息让内核知道它为何终止。最常见的是,状态 0 表示该过程成功。但是,这还不足以完全终止进程。父进程必须使用 wait 系统调用来确认子进程的终止,它的作用是检查子进程的终止状态。我知道想想就很可怕,但等待电话是必要的,毕竟哪个父母不想知道他们的孩子是怎么死的?

孤儿进程

当父进程在子进程之前死亡时,内核知道它不会收到 wait 调用,因此它会使这些进程成为“孤儿”并将它们置于 init 的照顾之下(记住所有进程的母亲)。 Init 最终将为这些孤儿执行等待系统调用,以便它们可以死亡。

僵尸进程

当子进程终止并且父进程尚未调用 wait 时会发生什么?我们仍然希望能够看到子进程是如何终止的,因此即使子进程完成,内核也会将子进程变成僵尸进程。子进程使用的资源仍然可以释放给其他进程,但是进程表中仍然有该僵尸进程的条目。僵尸进程也无法被杀死,因为它们在技术上是“死亡”的,所以你不能使用信号来杀死它们。最终如果父进程调用wait系统调用,僵尸进程就会消失,这就是所谓的“收割”。如果父进程不执行等待调用,init 将采用僵尸并自动执行等待并删除僵尸。僵尸进程过多可能是一件坏事,因为它们会占用进程表上的空间,如果进程表填满,则会阻止其他进程运行。

相关内容