SSH会话终止后,为什么我的Python后台进程结束?


19

我有一个bash脚本,它启动python3脚本(我们称之为startup.sh),其关键是:

nohup python3 -u <script> &

当我ssh直接进入并调用此脚本时,退出后,python脚本将继续在后台运行。但是,当我运行此命令时:

ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"

该过程一旦ssh完成运行就结束,并关闭会话。

两者有什么区别?

编辑:python脚本正在通过Bottle运行Web服务。

EDIT2:我还尝试创建一个初始化脚本该脚本调用startup.sh并运行ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>",但行为相同。

EDIT3:也许是脚本中的其他内容。这是大部分脚本:

chmod 700 ${key_loc}

echo "INFO: Syncing files."
rsync -azP -e "ssh -i ${key_loc} -o StrictHostKeyChecking=no" ${source_client_loc} ${remote_user}@${remote_hostname}:${destination_client_loc}

echo "INFO: Running startup script."
ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart"

EDIT4:当我在最后一行睡眠的情况下运行最后一行时:

ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart; sleep 1"

echo "Finished"

它永远不会到达echo "Finished",并且我看到了我从未见过的Bottle服务器消息:

Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.

如果我手动进行SSH并自己终止该进程,我会看到“完成”。

EDIT5:使用EDIT4,如果我向任何端点发出请求,我都会返回页面,但是Bottle错误出了:

Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.


----------------------------------------
Exception happened during processing of request from ('<IP>', 55104)

有什么办法可以让我们更多地了解python脚本的功能?如果没有完整的源代码,您可能仍然会猜测,但是更多地了解python脚本的作用可能有助于我们做出有根据的猜测。
布拉奇利2014年

是的-添加到问题。
neverendingqs 2014年

该脚本可能会在某种程度上尽早执行此操作,这取决于所连接的终端或类似的内容,并且可能是时间问题:如果会话持续到开始的前几秒钟,它将起作用,否则无效。strace如果您使用的是Linux或trussSolaris,则最好的选择是在其下运行,并查看其终止方式。例如ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> strace -fo /tmp/debug ./startup.sh
Celada 2014年

您是否尝试&在启动脚本的末尾使用?添加take &可以消除ssh会话对父ID的依赖性(当父ID死亡时,子ID也将消失)。此外,我认为这是一种基于重复的问题,这个以前的帖子。我在前一句中向您提交的帖子是帖子的副本,可能会提供更好的详细信息。
雅各布·布莱恩

我曾经尝试nohup ./startup.sh &过,但是它具有相同的行为。startup.sh已经包含一个叉子(nohup python3 -u <script> &),所以我很确定不需要再次叉子。
neverendingqs 2014年

Answers:


11

我会将命令与其标准输入/输出和错误流断开连接:

nohup python3 -u <script> </dev/null >/dev/null 2>&1 &  

ssh需要一个没有更多输出且不需要更多输入的指标。输入其他内容并重定向输出意味着ssh输入/输出不是来自终端,也不是去终端,因此可以可以安全地退出。这意味着输入必须来自其他位置,而输出(STDOUT和STDERR)都应该位于其他位置。

</dev/null部分指定/dev/null为输入<script>。为什么这在这里有用:

将/ dev / null重定向到stdin将立即给来自该进程的任何读取调用EOF。这通常对于将进程与tty分离(这样的进程称为守护程序)很有用。例如,通过ssh远程启动后台进程时,必须重定向stdin以防止进程等待本地输入。 /programming/19955260/what-is-dev-null-in-bash/19955475#19955475

或者,从另一个输入源重定向应该相对安全,只要当前 ssh会话不需要保持打开状态。

随着 >/dev/null部分壳体重定向标准输出到的/ dev / null的基本上丢弃它。>/path/to/file也可以。

最后一部分 2>&1是将STDERR重定向到STDOUT。

程序有三个标准的输入和输出源。如果标准输入是交互式程序,则通常来自键盘;如果正在处理另一个程序的输出,则通常来自另一个程序。该程序通常打印到标准输出,有时打印到标准错误。这三个文件描述符(您可以将它们视为“数据管道”)通常称为STDIN,STDOUT和STDERR。

有时他们没有被命名,而是被编号!它们的内置编号依次为0、1和2。默认情况下,如果您未明确命名或排名第一,则是在谈论STDOUT。

在这种情况下,您可以看到上面的命令将标准输出重定向到/ dev / null,在这里您可以转储不需要的任何内容(通常称为位桶),然后将标准错误重定向到标准输出(执行此操作时,您必须在目标前面加上&)。

因此,简短的解释是“该命令的所有输出都应该被塞进一个黑洞中。”这是使程序真正安静的一种好方法!
> / dev / null 2>&1是什么意思?| Xaprb


nohup python3 -u <script> >/dev/null 2>&1 &nohup python3 -u <script> > nohup.out 2>&1 &工作。我以为nohup会自动重定向所有输出-有什么区别?
neverendingqs 2014年

@neverendingqs,nohup您的远程主机上具有什么版本?POSIX nohup不需要重定向stdin,我错过了,但是它仍然应该重定向stdoutstderr
Graeme 2014年

好像我正在使用nohup (GNU coreutils) 8.21
neverendingqs 2014年

@neverendingqs,是否nohup打印任何消息,例如nohup: ignoring input and appending output to ‘nohup.out’
Graeme 2014年

是的-这是确切的信息。
neverendingqs 2014年

3

看一下man ssh

 ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
     [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L [bind_address:]port:host:hostport]
     [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
     [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]
     [user@]hostname [command]

在运行时,ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"您正在将外壳程序脚本startup.sh作为ssh命令运行。

根据描述:

如果指定了命令,那么它将在远程主机而不是登录shell上执行。

基于此,它应该远程运行脚本。

该命令与nohup python3 -u <script> &在本地终端中运行之间的区别在于,此命令作为本地后台进程运行,而ssh命令尝试将其作为远程后台进程运行。

如果要在本地运行脚本,则不要将startup.sh作为ssh命令的一部分运行。您可以尝试类似ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"

如果您打算远程运行脚本,并且希望该过程在ssh会话终止后继续进行,则必须首先screen在远程主机上启动会话。然后,您必须在屏幕中运行python脚本,并且在结束ssh会话后它将继续运行。

请参阅屏幕用户手册

虽然我认为screen是最佳选择,但如果必须使用nohup,请shopt -s huponexit在运行nohup命令之前考虑在远程主机上进行设置。或者,您可以使用disown -h [jobID]标记该进程,这样就不会将SIGHUP发送给它。1个

从后台的shell提示符退出后,如何继续运行作业?

SIGHUP(Hangup)信号由您的系统用于控制终端或控制过程终止。您也可以使用SIGHUP重新加载配置文件并打开/关闭日志文件。换句话说,如果您从终端注销,所有正在运行的作业将被终止。为了避免这种情况,您可以将-h选项传递给disown命令。此选项标记每个jobID,以便如果Shell收到SIGHUP,则不会将SIGHUP发送到该作业。

另外,请参阅此摘要,了解huponexit退出,杀死或丢弃外壳时的工作方式。我猜您当前的问题与Shell会话的结束方式有关。2

  1. 只有在设置了huponexit选项的情况下,关闭ssh连接时,所有通过ssh连接打开或未通过壳打开的shell子进程都会被SIGHUP杀死:运行shopt huponexit以查看是否为真。

  2. 如果huponexit为true,则可以使用nohup或disown将进程与Shell分离,这样在退出时不会被杀死。或者,通过屏幕运行内容。

  3. 如果huponexit为false(这是至少在某些Linux上最近的默认设置),那么后台注销的作业将不会在正常注销时被杀死。

  4. 但是,即使huponexit为false,如果ssh连接被终止或丢失(与正常注销不同),则后台进程仍将被终止。可以通过(2)中的disown或nohup避免这种情况。

最后,这是一些如何使用shopt huponexit的示例。3

$ shopt -s huponexit; shopt | grep huponexit
huponexit       on
# Background jobs will be terminated with SIGHUP when shell exits

$ shopt -u huponexit; shopt | grep huponexit
huponexit       off
# Background jobs will NOT be terminated with SIGHUP when shell exits

根据bash手册页,huponexit应该只影响交互式shell,而不会影响脚本-“如果已经使用shopt设置了huponexit shell选项,则当交互式登录shell退出时,bash会向所有作业发送SIGHUP。”
Graeme 2014年

2

也许-n在开始时值得尝试的 选择ssh?这将防止远程进程对local的依赖stdin,当然local 会在结束时立即关闭ssh session。每当尝试访问其价格时,这将导致远程价格终止stdin


尝试没有成功= [。
neverendingqs 2014年

2

我怀疑你有种族状况。它会像这样:

  • SSH连接开始
  • SSH启动startup.sh
  • startup.sh启动后台进程(nohup)
  • startup.sh完成
  • ssh完成,这会杀死子进程(即,nohup)

如果ssh没有缩短内容,则会发生以下情况(不确定这两个命令的顺序):

  • nohup启动您的python脚本
  • nohup与父进程和终端断开连接。

因此最后两个关键步骤不会发生,因为startup.sh和ssh在nohup有时间做其事情之前就完成了。

我希望,如果您在startup.sh末尾放了几秒钟的睡眠,您的问题将会消失。我不确定您需要多少时间。如果将其最小化很重要,那么也许您可以查看proc中的某些内容以查看何时是安全的。


好点,不要以为这个窗口会很长-可能只有几毫秒。您可以检查/proc/$!/comm是不是nohup还是更方便地使用的输出ps -o comm= $!
Graeme 2014年

这对于正常注销应该有效,但是会话被删除或终止时该怎么办?您是否还需要取消工作,以免被长叹工作完全忽略?
iyrin 2014年

@RyanLoremIpsum:启动脚本只需要等待足够长的时间,以使子进程完全分离。之后,ssh会话发生什么都无关紧要。如果发生这种情况时,如果其他原因在简短的窗口中杀死了ssh会话,则您无能为力。
mc0e 2014年

@Graeme是的,我想这是非常快的,但是我对Nohup的确切功能还不了解。指向权威(或至少是知识和详细)资料来源的指针将很有用。
mc0e 2014年


1

这听起来更像是python脚本或脚本python本身在做什么。nohup实际上所做的一切(简化重定向的操作)只是在运行程序之前将HUP信号的处理程序设置为SIG_IGN(忽略)。SIG_DFL一旦开始运行,就没有什么可以阻止程序将其设置回或安装其自己的处理程序的了。

您可能想尝试的一件事是将命令括在圆括号中,以便获得双叉效果,并且python脚本不再是Shell进程的子级。例如:

( nohup python3 -u <script> & )

另一件事也值得一试(如果您正在使用,bash而不是其他shell),那就是使用disown内置函数而不是nohup。如果一切正常,实际上应该没有什么区别,但是在交互式外壳中,这将阻止HUP信号传播到python脚本中。您可以在下一行或以下相同的行中添加被拒绝的内容(请注意,在a ;之后添加a &是错误bash):

python3 -u <script> </dev/null &>/dev/null & disown

如果以上方法或某些方法的组合均不起作用,那么肯定可以解决该问题的唯一地方就是python脚本本身。


双叉效果是否足够(基于@RyanLoremIpsum的答案)?
neverendingqs 2014年

两者都没有解决问题= [。如果是Python问题,那么您对从哪里开始调查有想法(不能在此处发布过多的Python脚本)?
neverendingqs 2014年

@neverendingqs,如果您的意思是说huponexit,在子shell中运行应该具有与disown不会将进程添加到作业列表中相同的效果。
Graeme 2014年

@neverendingqs,更新了我的答案。忘记了您应该使用重定向disown。不要指望它会带来很大的不同。我认为您最好的选择是更改python脚本,以便告诉您退出的原因。
Graeme 2014年

重定向输出有效(unix.stackexchange.com/a/176610/52894),但是我不确定显式执行与执行之间的区别nohup
neverendingqs 2014年

0

我认为这是因为工作与会议相关。一旦结束,所有用户作业也将结束。


2
但是,为什么这与获得终端,键入并运行命令并退出有什么不同?一旦我关闭,两个会话都关闭。
neverendingqs 2014年

同意,我想了解为什么这与手动关闭自己的终端没有什么不同。
Avindra Goolcharan 2014年

0

如果nohup可以打开其输出文件,则可能有一个提示nohup.outpython通过运行脚本时,路径可能不在路径中ssh

我会尝试为该命令创建一个日志文件。尝试使用:

nohup /usr/bin/python3 -u <script> &>logfile &

我用来ssh手动运行脚本,所以我假设python3在路径中。
neverendingqs 2014年

@neverendingqs日志文件是否包含任何内容?
BillThor

没有什么不同寻常的-启动看起来很正常。
neverendingqs 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.