当收到一个信号时,如何杀死 Bash 脚本前台子进程

12

我正在编写一个类似于这样的bash脚本包装fastcgi应用程序:

#!/bin/bash
# stuff
./fastcgi_bin
# stuff

由于 bash 只在前台脚本结束时执行信号陷阱,因此我不能仅仅使用 kill -TERM scriptpid 命令,因为 fastcgi 应用程序将继续运行。
我尝试将二进制文件发送到后台执行:

#!/bin/bash
# stuff
./fastcgi_bin &
PID=$!
trap "kill $PID" TERM
# stuff

但是,如果我像这样做,显然标准输入和输出没有被正确重定向,因为它不能连接到lighttpd的mod_fastcgi,前台版本可以正常工作。

编辑:我一直在研究这个问题,这是因为当一个程序在后台启动时,bash将/dev/null重定向到stdin,所以任何避免这种情况的方法也应该解决我的问题。

有没有提示如何解决这个问题?

7个回答

16

我想到了一些选项:

  • 当从shell脚本启动进程时,父进程和子进程属于同一进程组。杀死父进程将使子进程保持活动状态,因此应该杀死整个进程组。可以通过将取反的PGID(进程组ID)传递给kill来实现,这与父进程的PID相同。例如:kill -TERM -$PARENT_PID

  • 不要将二进制文件作为子进程执行,而是使用exec替换脚本进程。但是,您会失去执行后续操作的能力,因为 exec 完全替换了父进程。

  • 不要杀死shell脚本进程,而是杀死FastCGI二进制文件。然后,在脚本中检查返回代码并根据情况采取行动。例如:./fastcgi_bin || exit -1

根据mod_fastcgi处理工作程序的方式,只有第二种选项可能可行。


那也是我的第一反应:killpg - tchrist
您也可以考虑获取其他脚本。这样可以在同一进程中运行,而不会消除当前脚本的其余部分。(例如. ./fastcgi.bin) (即<句点><空格><命令>) - GinoA

1

我不知道这对你是否可行,但既然你有赏金,我假设你可能会考虑一些非传统的想法。

你能否用Perl重写bash脚本?Perl有几种管理子进程的方法。你可以阅读perldoc perlipc以及核心模块IPC::Open2IPC::Open3中的更多细节。

我不知道这将如何与lighttpd等接口,或者这种方法是否具有更多功能,但至少它为你提供了更多的灵活性和更多的阅读材料。


1

我刚刚写了这个脚本,用于杀死一个bash脚本及其所有子进程...

#!/bin/bash
# This script will kill all the child process id for a  given pid
# based on http://www.unix.com/unix-dummies-questions-answers/5245-script-kill-all-child-process-given-pid.html

ppid=$1

if [ -z $ppid ] ; then
   echo "This script kills the process identified by pid, and all of its kids";
   echo "Usage: $0 pid";
   exit;
fi

for i in `ps j | awk '$3 == '$ppid' { print $2 }'`
do
    $0 $i   
    kill -9 $i
done

确保脚本可执行,否则您将在 $0 $i 上收到错误信息。


1

我不确定我完全理解你的观点,但这是我尝试过的,这个过程似乎能够管理陷阱(称其为trap.sh):

#!/bin/bash

trap "echo trap activated" TERM INT
echo begin
time sleep 60
echo end

开始吧:

./trap.sh &

并尝试使用它(同时只能使用其中一个命令):

kill -9 %1
kill -15 %1

或者在前台启动:

./trap.sh

并使用控制-C 中断。

对我来说似乎有效。 你具体遇到了什么问题?


kill 命令和终端信号都会发送到整个进程组,因此会影响 shell 和 sleep。顺便问一下,你为什么要尝试捕获 SIGKILL 信号? - jilles
@jilles:好的,我已经编辑了帖子以匹配您的评论。 - asoundmove

0

您可以通过自己重定向stdin来覆盖后台进程的隐式</dev/null,例如:

sh -c 'exec 3<&0; { read x; echo "[$x]"; } <&3 3<&- & exec 3<&-; wait'

0

尝试使用 ./fastcgi_bin 0<&0 & 保留原始的 stdin

#!/bin/bash
# stuff
./fastcgi_bin 0<&0 &
PID=$!./fastcgi_bin 0<&0 &
trap "kill $PID" TERM
# stuff


# test
#sh -c 'sleep 10 & lsof -p ${!}'
#sh -c 'sleep 10 0<&0 & lsof -p ${!}'

-1

你可以使用协处理器来实现。

编辑:好吧,协处理器是后台进程,可以打开标准输入和输出(因为bash为它们准备了FIFO)。但是你仍然需要读/写这些FIFO,并且唯一有用的原语是bash的read(可能带有超时或文件描述符);对于cgi来说不够健壮。所以经过再次考虑,我的建议是不要在bash中执行此操作。在fastcgi中或在类似WSGI的http包装器中完成额外的工作会更方便。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接