【问题标题】:How to prevent a background process from being stopped after closing SSH client in Linux如何防止在 Linux 中关闭 SSH 客户端后停止后台进程
【发布时间】:2023-05-19 15:39:01
【问题描述】:

我正在通过 SSH (Putty) 在 Linux 机器上工作。我需要让一个进程在夜间运行,所以我想我可以通过在后台启动进程(在命令末尾带有一个&符号)并将标准输出重定向到一个文件来做到这一点。

令我惊讶的是,这不起作用。一旦我关闭 Putty 窗口,该过程就会停止。

我怎样才能防止这种情况发生??

【问题讨论】:

标签: linux process ssh background-process


【解决方案1】:

查看“nohup”程序。

【讨论】:

  • 事后如何阻止?
  • 登录并执行“杀死 ”。如果您不知道 pid,请使用“pidof”。
  • 您可以使用nohup command > /dev/null 2>&1 &在后台运行,而无需创建任何stdout或stderr输出(没有nohup.out文件)
  • 由于我很懒而且不擅长记住神秘的字符序列,所以我根据@KCD 所说的写了this,并且一直在使用它。
  • 这在我这边不起作用,因为这并不能保护程序在 ctty 关闭时获得 SIGHUP。我有一个永远不能接收 SIGHUP 的程序。 nohup 不起作用,因为它不会阻止 SIGHUP,它只是默认忽略它,不需要占上风。由于我没有screentmuxat 或类似的东西,我需要一种在shell 级别上确定将程序与ctty 分离的方法。我发现的唯一方法是使用ssh -T remote '(program&)&' 启动程序,这使得在交互式ssh 会话中无法后台程序。
【解决方案2】:

我建议使用GNU Screen。它允许您在所有进程继续运行时断开与服务器的连接。在我知道它存在之前,我不知道没有它我是如何生活的。

【讨论】:

  • 这是我用过的最棒的软件之一。严重地。我让它在一个 BSD 盒子上运行,我可以从任何地方 ssh 进入它,并且可以简单地重新连接到我的屏幕,并让我的所有终端都在那里我正在做各种各样的事情。
  • 我可以证明这一点。屏幕是一个很棒的应用程序。重新连接的能力令人惊叹,并节省了大量可能丢失的工作。
  • 取决于您是否需要重新连接到后台应用程序。如果你这样做,那么,是的,屏幕是唯一的飞行方式。但是,如果它是即发即弃的,那么 nohup 也很符合要求,如果不是更好的话。
  • +1 用于屏幕。或者,作为替代方案,tmux(我更喜欢这个而不是 screen)甚至 byobu,它是 screen 或 tmux 的一个不错的前端。您可以只键入 screen 以获取要使用的 shell 并稍后随时返回,或者使用 screen 运行命令,例如“screen command”:只要进程“command”存在,screen session 就会存在,如果它是某事很长,你可以随时回去看看它的标准输出。
  • Linode 网站提供了一个很好的 introduction 如何使用 screen
【解决方案3】:

当会话关闭时,进程接收到它显然没有捕捉到的 SIGHUP 信号。您可以在启动进程时使用nohup命令或在启动进程后使用bash内置命令disown -h来防止这种情况发生:

> help disown
disown: disown [-h] [-ar] [jobspec ...]
     By default, removes each JOBSPEC argument from the table of active jobs.
    If the -h option is given, the job is not removed from the table, but is
    marked so that SIGHUP is not sent to the job if the shell receives a
    SIGHUP.  The -a option, when JOBSPEC is not supplied, means to remove all
    jobs from the job table; the -r option means to remove only running jobs.

【讨论】:

  • 这里的好处是 disown 适用于已经启动的进程。
  • 'jobspec' 是指 pid 吗?
  • 别担心,在这里找到答案*.com/questions/625409/…
【解决方案4】:

守护进程?没有?屏幕? (tmux ftw,屏幕是垃圾;-)

只要做其他应用从一开始就做的事情——双叉。

# ((exec sleep 30)&)
# grep PPid /proc/`pgrep sleep`/status
PPid:   1
# jobs
# disown
bash: disown: current: no such job

砰!完成:-) 我在所有类型的应用程序和许多旧机器上都使用过无数次。您可以结合重定向和诸如此类的东西在您和流程之间打开一个私人通道。

创建为 coproc.sh:

#!/bin/bash

IFS=

run_in_coproc () {
    echo "coproc[$1] -> main"
    read -r; echo $REPLY
}

# dynamic-coprocess-generator. nice.
_coproc () {
    local i o e n=${1//[^A-Za-z0-9_]}; shift
    exec {i}<> <(:) {o}<> >(:) {e}<> >(:)
. /dev/stdin <<COPROC "${@}"
    (("\$@")&) <&$i >&$o 2>&$e
    $n=( $o $i $e )
COPROC
}

# pi-rads-of-awesome?
for x in {0..5}; do
    _coproc COPROC$x run_in_coproc $x
    declare -p COPROC$x
done

for x in COPROC{0..5}; do
. /dev/stdin <<RUN
    read -r -u \${$x[0]}; echo \$REPLY
    echo "$x <- main" >&\${$x[1]}
    read -r -u \${$x[0]}; echo \$REPLY
RUN
done

然后

# ./coproc.sh 
declare -a COPROC0='([0]="21" [1]="16" [2]="23")'
declare -a COPROC1='([0]="24" [1]="19" [2]="26")'
declare -a COPROC2='([0]="27" [1]="22" [2]="29")'
declare -a COPROC3='([0]="30" [1]="25" [2]="32")'
declare -a COPROC4='([0]="33" [1]="28" [2]="35")'
declare -a COPROC5='([0]="36" [1]="31" [2]="38")'
coproc[0] -> main
COPROC0 <- main
coproc[1] -> main
COPROC1 <- main
coproc[2] -> main
COPROC2 <- main
coproc[3] -> main
COPROC3 <- main
coproc[4] -> main
COPROC4 <- main
coproc[5] -> main
COPROC5 <- main

然后你去,生成任何东西。 sleep 1 而不是:,因为它有点活泼,而且我会收到“文件忙”错误——如果运行真正的命令(例如,command true),则永远不会发生

“heredoc 采购”:

. /dev/stdin <<EOF
[...]
EOF

这适用于我尝试过的每一个 shell,包括busybox/etc (initramfs)。我以前从未见过它,我在刺激时独立发现它,谁知道source可以接受args?但它通常作为一种更易于管理的 eval 形式,如果有的话。

【讨论】:

  • 为什么投反对票...如果问题是旧的怎么办;考虑到还有 11 个其他答案很糟糕,这显然是相关的。在过去 30 年中,这种解决方案是无系统的,是惯用且公认的守护进程方式,而不是无意义的应用程序,例如。 nohup 等人。
  • 无论您的答案有多好,有时 SO 上的某些人不会喜欢它并会投反对票。最好不要太担心。
  • @tbc0 ...试试ssh myhost "((exec sleep 500)&amp;) &gt;/dev/null"
  • @anthonyrisinger 好的,这行得通。我认为这更干净:ssh myhost 'sleep 500 &gt;&amp;- 2&gt;&amp;- &lt;&amp;- &amp;'TMTOWTDI ;)
  • 这很棒。在busybox中实际工作的唯一解决方案。它值得更多的支持
【解决方案5】:
nohup blah &

用你的进程名代替blah!

【讨论】:

  • 您可能想要添加重定向标准输出和标准错误。
  • nohup 将 stdout 和 stderr 重定向到 nohup.out(或 nohup.out 和 nohup.err 取决于版本),因此除非您运行多个命令,否则没有必要。
【解决方案6】:

就个人而言,我喜欢“批处理”命令。

$ batch
> mycommand -x arg1 -y arg2 -z arg3
> ^D

这会将其填充到后台,然后将结果邮寄给您。它是 cron 的一部分。

【讨论】:

    【解决方案7】:

    正如其他人所指出的,要在后台运行进程以便您可以断开与 SSH 会话的连接,您需要让后台进程正确地与其控制终端解除关联 - 这是 SSH 会话的伪 tty用途。

    您可以在 Stevens 的“Advanced Network Program, Vol 1, 3rd Edn”或 Rochkind 的“Advanced Unix Programming”等书籍中找到有关守护进程的信息。

    我最近(在过去的几年里)不得不处理一个没有正确守护自己的顽固程序。我最终通过创建一个通用的守护程序来解决这个问题 - 类似于 nohup 但有更多可用的控件。

    Usage: daemonize [-abchptxV][-d dir][-e err][-i in][-o out][-s sigs][-k fds][-m umask] -- command [args...]
      -V          print version and exit
      -a          output files in append mode (O_APPEND)
      -b          both output and error go to output file
      -c          create output files (O_CREAT)
      -d dir      change to given directory
      -e file     error file (standard error - /dev/null)
      -h          print help and exit
      -i file     input file (standard input - /dev/null)
      -k fd-list  keep file descriptors listed open
      -m umask    set umask (octal)
      -o file     output file (standard output - /dev/null)
      -s sig-list ignore signal numbers
      -t          truncate output files (O_TRUNC)
      -p          print daemon PID on original stdout
      -x          output files must be new (O_EXCL)
    

    双破折号在不使用 GNU getopt() 函数的系统上是可选的;在 Linux 等系统上是必需的(或者您必须在环境中指定 POSIXLY_CORRECT)。由于双破折号无处不在,因此最好使用它。

    如果您想要daemonize 的来源,您仍然可以联系我(gmail dot com 上的 firstname dot lastname)。

    但是,代码现在(终于)可以在 GitHub 上的我的SOQ(堆栈 溢出问题)存储库作为文件daemonize-1.10.tgzpackages 子目录。

    【讨论】:

    • 为什么不把源码放到github或者bitbucket上呢?
    • 为什么没有来自 github 的源代码需要反对?
    • @JonathanLeffler 恕我直言,列出一个不以任何形式(甚至不是商业形式)公开可用的程序的所有酷选项几乎是在浪费读者的时间。
    【解决方案8】:

    对于大多数进程,您可以使用这个旧的 Linux 命令行技巧来伪守护进程:

    # ((mycommand &)&)
    

    例如:

    # ((sleep 30 &)&)
    # exit
    

    然后启动一个新的终端窗口并:

    # ps aux | grep sleep
    

    将显示sleep 30 仍在运行。

    您所做的是将进程作为子进程启动,当您退出时,通常会触发进程退出的nohup 命令不会级联到孙子进程,而将其保留为一个孤儿进程,仍在运行。

    我更喜欢这种“设置并忘记它”的方法,不需要处理nohupscreen、tmux、I/o 重定向或任何类似的东西。

    【讨论】:

      【解决方案9】:

      在基于 Debian 的系统上(在远程机器上) 安装:

      sudo apt-get install tmux

      用法:

      tmux

      运行你想要的命令

      要重命名会话:

      Ctrl+B 然后 $

      设置名称

      退出会话:

      Ctrl+B 然后 D

      (这将离开 tmux 会话)。然后,您可以退出 SSH。

      当你需要回来/再次查看时,启动SSH,然后进入

      tmux 附加会话名称

      它将带你回到你的 tmux 会话。

      【讨论】:

      • 这是要走的路
      【解决方案10】:

      如果您使用 screen 以 root 身份运行进程,请注意特权提升攻击的可能性。如果您自己的帐户以某种方式受到损害,将会有一种直接的方式来接管整个服务器。

      如果此进程需要定期运行并且您在服务器上有足够的访问权限,更好的选择是使用 cron 运行作业。您还可以使用 init.d(超级守护程序)在后台启动您的进程,并且它可以在完成后立即终止。

      【讨论】:

        【解决方案11】:

        nohup 非常适合您将详细信息记录到文件中。但是当它进入后台时,如果您的脚本要求,您无法为其提供密码。我认为您必须尝试screen。它是一个实用程序,您可以使用 yum 安装在您的 linux 发行版上,例如在 CentOS yum install screen 上,然后通过 putty 或其他软件访问您的服务器,在您的 shell 类型 screen 中。它将在腻子中打开屏幕 [0]。做你的工作。您可以在同一个腻子会话中创建更多屏幕[1]、屏幕[2]等。

        你需要知道的基本命令:

        开始屏幕

        屏幕


        c创建下一个屏幕

        ctrl+a+c


        移动到n您创建的下一个屏幕

        ctrl+a+n


        d蚀刻

        ctrl+a+d


        在工作期间关闭您的腻子。下次你通过腻子类型登录时

        屏幕-r

        重新连接到您的屏幕,您可以看到您的进程仍在屏幕上运行。并退出屏幕类型#exit。

        更多详情请见man screen

        【讨论】:

        • 假设yum 是正确的工具,如果您不了解发行版,那就不好了。你应该明确哪些发行版screen可以与yum一起安装。
        【解决方案12】:

        Nohup 允许在父进程被杀死的情况下不杀死客户端进程,作为注销时的参数。最好还是使用:

        nohup /bin/sh -c "echo \$\$ > $pidfile; exec $FOO_BIN $FOO_CONFIG  " > /dev/null
        

        Nohup 使您启动的进程免受终止,您的 SSH 会话及其子进程在您注销时被终止。我给出的命令为您提供了一种将应用程序的 pid 存储在 pid 文件中的方法,以便您以后可以正确地杀死它并允许该进程在您注销后运行。

        【讨论】:

          【解决方案13】:

          使用屏幕。它使用起来非常简单,就像终端的 vnc 一样工作。 http://www.bangmoney.org/presentations/screen.html

          【讨论】:

            【解决方案14】:

            如果您也愿意运行 X 应用程序 - 将 xpra 与“screen”一起使用。

            【讨论】:

              【解决方案15】:

              我也会选择屏幕程序(我知道其他人的答案是屏幕,但这是一个完成)

              不仅 &、ctrl+z bg disown、nohup 等可能会给你一个令人讨厌的惊喜,即当你注销工作时仍然会被杀死(我不知道为什么,但它确实发生在我身上,而且它没有'不要打扰它,因为我切换到使用屏幕,但我猜 anthonyrisinger 解决方案,因为双分叉会解决这个问题),屏幕也有一个 主要 优势超过背景:

              screen will background your process without losing interactive control to it
              

              顺便说一句,这是我一开始就不会问的问题 :) ...我从一开始就在任何 unix 中做任何事情时都使用 screen ...我(几乎)从不在 unix/linux shell 中工作没有先启动屏幕...我现在应该停下来,否则我将开始无休止地介绍什么是好屏幕以及可以为您做什么...自己查一下,这是值得的;)

              【讨论】:

              • PS anthonyrisinger,你很好,我给你,但是...... 30年?我敢打赌,当 &、bg、nohup 或 screen 还没有出现时,这是一个解决方案,并且没有冒犯我感谢您的知识,但是使用它太复杂了:)
              • (aside: see Tmux) 尽管这大大早于我 [1987],&amp;(异步执行)是由 the Thompson shell in 1971 引入的,用于 UNIX 的 first 版本... so it literally "has always been" ;-) 唉,我太保守了——实际上已经 41 年了。
              【解决方案16】:

              还有开源 libslack 包的daemon 命令。

              daemon 非常可配置,并且确实关心所有繁琐的守护进程,例如自动重启、日志记录或 pidfile 处理。

              【讨论】:

                【解决方案17】:

                将此字符串附加到您的命令中:>&- 2>&- &- 表示关闭标准输出。 2>&- 表示关闭标准错误。

                $ ssh myhost 'sleep 30 >&- 2>&- <&- &'
                # ssh returns right away, and your sleep job is running remotely
                $
                

                【讨论】:

                  【解决方案18】:

                  我使用了屏幕命令。此链接详细说明了如何执行此操作

                  https://www.rackaid.com/blog/linux-screen-tutorial-and-how-to/#starting

                  【讨论】:

                    【解决方案19】:

                    接受的答案建议使用 nohup。我宁愿建议使用pm2。使用 pm2 而不是 nohup 有很多优点,例如保持应用程序的活动性、维护应用程序的日志文件以及更多其他功能。更多详情check this out

                    要安装 pm2,您需要下载 npm。对于基于 Debian 的系统

                    sudo apt-get install npm
                    

                    对于 Redhat

                    sudo yum install npm
                    

                    或者你可以关注these instruction。 安装 npm 后使用它来安装 pm2

                    npm install pm2@latest -g
                    

                    完成后,您可以通过

                    启动您的应用程序
                    $ pm2 start app.js              # Start, Daemonize and auto-restart application (Node)
                    $ pm2 start app.py              # Start, Daemonize and auto-restart application (Python)
                    

                    对于进程监控,使用以下命令:

                    $ pm2 list                      # List all processes started with PM2
                    $ pm2 monit                     # Display memory and cpu usage of each app
                    $ pm2 show [app-name]           # Show all informations about application
                    

                    使用应用名称或进程 ID 管理进程或一起管理所有进程:

                    $ pm2 stop     <app_name|id|'all'|json_conf>
                    $ pm2 restart  <app_name|id|'all'|json_conf>
                    $ pm2 delete   <app_name|id|'all'|json_conf>
                    

                    日志文件可以在

                    中找到
                    $HOME/.pm2/logs #contain all applications logs
                    

                    二进制可执行文件也可以使用 pm2 运行。您必须对 jason 文件进行更改。将"exec_interpreter" : "node" 更改为"exec_interpreter" : "none".(参见attributes section)。

                    #include <stdio.h>
                    #include <unistd.h>  //No standard C library
                    int main(void)
                    {
                        printf("Hello World\n");
                        sleep (100);
                        printf("Hello World\n");
                    
                        return 0;
                    }
                    

                    编译以上代码

                    gcc -o hello hello.c  
                    

                    并在后台使用 np2 运行它

                    pm2 start ./hello
                    

                    【讨论】:

                    • 这个可以用来运行二进制可执行文件吗?
                    • @GetFree;是的。你可以。
                    • 请添加示例。现在的答案看起来只适用于脚本文件。
                    • @GetFree;添加了示例。如果您有任何问题,请告诉我。
                    【解决方案20】:

                    在 systemd/Linux 上,systemd-run 是启动独立于会话的进程的好工具。

                    【讨论】: