【问题标题】:How can I launch a new process that is NOT a child of the original process?如何启动不是原始进程子进程的新进程?
【发布时间】:2013-12-18 17:50:34
【问题描述】:

(OSX 10.7) 我们使用的应用程序让我们分配脚本以在应用程序中发生某些活动时调用。我已经分配了一个 bash 脚本并且它正在被调用,问题是我需要做的是执行一些命令,等待 30 秒,然后再执行一些命令。如果我让我的 bash 脚本执行“睡眠 30”,那么整个应用程序会在等待我的脚本完成时冻结 30 秒。

我尝试将 30 秒的等待(和第二组命令)放入单独的脚本并调用“./secondScript &”,但应用程序仍然在那里等待 30 秒,什么也不做。我假设应用程序正在等待脚本和所有子进程终止。

我已经尝试了这些变体来从主脚本中调用第二个脚本,它们都有同样的问题:

  • nohup ./secondScript &
  • ( ( ./secondScript & ) & )
  • ( ./secondScript & )
  • nohup 脚本 -q /dev/null secondScript &

我无法更改应用程序并告诉它启动我的脚本而不是等待它完成。

如何启动一个进程(我希望该进程使用脚本语言)以使新进程不是当前进程的子进程?

谢谢, 克里斯

附言我尝试了“拒绝”命令,它也没有帮助。我的主脚本如下所示:

[initial commands]
echo Launching second script
./secondScript &
echo Looking for jobs
jobs
echo Sleeping for 1 second
sleep 1
echo Calling disown
disown
echo Looking again for jobs
jobs
echo Main script complete

我得到的输出是这样的:

Launching second script
Looking for jobs
[1]+ Running ./secondScript &
Sleeping for 1 second
Calling disown
Looking again for jobs
Main script complete

此时调用应用程序会在那里停留 45 秒,等待 secondScript 完成。

pps

如果在主脚本的顶部执行“ps”,它返回的唯一内容是我在单独的终端窗口中打开的交互式 bash 会话的进程 ID。

$SHELL 的值为 /bin/bash

如果我执行“ps -p $$”,它会正确告诉我

PID   TTY TIME    CMD
26884 ??  0:00.00 mainScript

如果我执行“lsof -p $$”,它会给我各种结果(我没有在此处粘贴所有列,假设它们不相关):

FD   TYPE   NAME
cwd  DIR    /private/tmp/blahblahblah
txt  REG    /bin/bash
txt  REG    /usr/lib/dyld
txt  REG    /private/var/db/dyld/dyld_shared_cache_x86_64
0    PIPE   
1    PIPE   -> 0xffff8041ea2d10
2    PIPE   -> 0xffff 8017d21cb
3r   DIR    /private/tmp/blahblah
4r   REG    /Volumes/DATA/blahblah
255r REG    /Volumes/DATA/blahblah

【问题讨论】:

  • mainScript 的父级是什么?
  • mainScript 正在从未命名的应用程序 (CVS) 启动。你可以告诉 CVS“当用户提交文件时启动我的脚本”。
  • 在这种情况下,您的脚本没有直接在 bash shell 上运行,而应用程序正在执行 shell 职责,这就解释了很多。您将不得不求助于在应用程序的进程树之外启动缓慢的进程。
  • 我认为 CVS 只是在做一个系统(“/bin/bash mainScript.sh”),CVS 甚至可以承担什么“shell 职责”?
  • 如果 CVS 只是在做 (/bin/bash mainScript) 你会在 pstree -p 看到启动的 bash shell 作为 mainScript 的父级,而 CVS 作为那个 bash shell 的父级。如果 CVS 是 mainScript 的直接父级,则不涉及 bash shell。 CVS 会做的是以一种非常简陋的方式“模仿”bash 来拦截每个系统调用。

标签: linux bash macos daemon


【解决方案1】:

在 Unix 中执行此操作的典型方法是双分叉。在 bash 中,您可以使用

( sleep 30 & )

(..) 创建一个子进程,& 创建一个孙进程。当子进程死亡时,子进程由 init 继承。


如果这不起作用,那么您的应用程序没有等待子进程。

它可能正在等待的其他事情包括会话和打开锁定文件:

要创建一个新会话,Linux 有一个setsid。在 OS X 上,您可能可以通过 script 来完成,顺便还会创建一个新会话:

# Linux:
setsid sleep 30

# OS X:
nohup script -q -c 'sleep 30' /dev/null &

要查找继承的文件描述符列表,您可以使用lsof -p yourpid,它将输出如下内容:

sleep   22479 user    0u   CHR 136,32      0t0       35 /dev/pts/32
sleep   22479 user    1u   CHR 136,32      0t0       35 /dev/pts/32
sleep   22479 user    2u   CHR 136,32      0t0       35 /dev/pts/32
sleep   22479 user    5w   REG  252,0        0  1048806 /tmp/lockfile

在这种情况下,除了标准的 FD 0、1 和 2 之外,您还可以打开一个 fd 5 并带有父级可以等待的锁定文件。

要关闭 fd 5,您可以使用exec 5>&-。如果您认为锁定文件本身可能是 stdin/stdout/stderr,您可以使用 nohup 将它们重定向到其他内容。

【讨论】:

  • 抱歉,我的更新和您的回复有交叉...我尝试添加行 ( ( script & ) & ) 并得到相同的结果。
【解决方案2】:

另一种方式是抛弃孩子

#!/bin/bash

yourprocess &

disown

据我了解,该应用程序替换了普通的 bash shell,因为即使 init 应该处理这个子进程,它仍在等待进程完成。 可能是“应用程序”拦截了通常由init 完成的孤儿处理。

在这种情况下,只有具有某些 IPC 的并行进程才能提供解决方案(请参阅我的其他答案)

【讨论】:

  • 唉,OSX 没有“拒绝”命令。 [对不起,我的错,disown 是一个 bash 命令,只是没有手册页。但我还不能让它工作,因为我找不到它的文档]
  • @BettyCrokker: info bash 并搜索“disown”,或查看here
  • 酷,谢谢!所以下一个问题......我假设 thorn 的答案主要是伪代码,我实际上不能在没有任何选项的情况下调用 disown。看来我需要将工作 ID 传递给它,我正在网上搜索以弄清楚我如何获得工作 ID ...
  • 不,它不是伪代码。您不需要工作 ID。 disown 只是从它的本地进程表中根除它的所有孩子。当然,您必须将yourprocess 替换为您的进程名称。
  • 当我说“我实际上不能在没有任何选项的情况下调用 disown”时,我的意思是“当我在没有任何选项的情况下调用 disown 时,它会给我一条错误消息“disown: current: no such job”
【解决方案3】:

我认为这取决于您的父进程如何尝试检测您的子进程是否已完成。 在我的情况下(我的父进程是 gnu make),我通过关闭 stdout 和 stderr (稍微基于answer of that other guy)成功,如下所示:

sleep 30 >&- 2>&- &

你也可以关闭标准输入

sleep 30 <&- >&- 2>&- &

或另外否认您的子进程(不适用于 Mac)

sleep 30 <&- >&- 2>&- & disown

目前仅在 kubuntu 14.04 和 Mac OSX 上的 bash 中进行测试。

【讨论】:

    【解决方案4】:

    如果一切都失败了:

    1. 创建命名管道

    2. 从“应用程序”启动“慢”脚本独立,确保在无限循环中执行它的任务,从管道读取开始。当它试图读取时,它会被读取阻塞..

    3. 从应用程序中,启动您的其他脚本。当它需要调用“慢”脚本时,只需将一些数据写入管道。慢速脚本将独立启动,因此您的脚本不会等待“慢速”脚本完成。

    那么,回答这个问题:
    bash - 我如何启动一个不是原始进程子进程的新进程?

    简单:不要启动它,而是让一个独立实体在启动期间启动它...例如init 或使用命令atbatch 即时启动它

    【讨论】:

    • 非常好,谢谢!只有这对我有用。了解了mkfifocool :)。无论我尝试什么,从我找到的任何答案中,disownnohup 都无法与 chromium 和 google-chrome 一起使用,也可能是 cairo-dock。
    • 这也有效(someCommand&amp;disown)&amp;disown 但我仍然不明白为什么(除了显而易见的),无论如何我把它和你建议的听众混在一起了:)
    【解决方案5】:

    我有一个贝壳

    └─bash(13882)
    

    我从哪里开始这样的过程:

    $ (urxvt -e ssh somehost&)
    

    我得到一个进程树(这个输出截自pstree -p):

    ├─urxvt(14181)───ssh(14182)
    

    进程在 pid 1 下的父级(在我的情况下为systemd)。

    但是,如果我这样做了(注意&amp; 的位置):

    $ (urxvt -e ssh somehost)&
    

    那么该进程将是 shell 的子进程:

    └─bash(13882)───urxvt(14181)───ssh(14182)
    

    在这两种情况下,shell 提示符都会立即返回,我可以exit 无需终止我在上面启动的进程树。

    对于后一种情况,进程树在 pid 1 下被重新定义为 shell 退出了,所以它和第一个例子一样。

    ├─urxvt(14181)───ssh(14182)
    

    无论哪种方式,结果都是一个比 shell 寿命更长的进程树。这 唯一的区别是该进程树的初始父级。

    供参考,也可以使用

    • nohup urxvt -e ssh somehost &amp;
    • urxvt -e ssh somehost &amp; disown $!

    两者都给出了与上面第二个示例相同的进程树。

    └─bash(13882)───urxvt(14181)───ssh(14182)
    

    当 shell 终止时,进程树会像以前一样重新设置父级 到 pid 1。

    nohup另外将进程的标准输出重定向到一个文件 nohup.out 所以,如果这是一个有用的特征,它可能是一个更有用的选择。

    否则,使用上面的第一种形式,您将立即拥有一个完全 分离的进程树。

    【讨论】:

      猜你喜欢
      • 2012-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-07
      相关资源
      最近更新 更多