控制哪个进程被Ctrl + C取消


13

我有一张能引导至Linux并运行小型Bash脚本的现场CD。该脚本搜索并运行第二个程序(通常是已编译的C ++二进制文件)。

您应该可以通过按Ctrl+ 中止第二个程序C。什么应该发生的是,第二个节目暂停,和bash脚本运行继续清理。什么实际发生的是主应用程序和bash脚本都终止。这是一个问题。

因此,我使用trap内置函数告诉Bash忽略SIGINT。现在Ctrl+ C终止C ++应用程序,但是Bash继续运行。大。

哦,是的...有时“第二个应用程序”是另一个Bash脚本。在那种情况下,Ctrl+ C现在什么也不做

显然,我对这些工作原理的理解是错误的……当用户按下Ctrl+ 时,如何控制哪个进程获取SIGINT C?我只想将这个信号引导到一个特定的过程

Answers:


11

经过许多小时的搜索,人们终于找到了答案。

  1. Linux具有进程组的概念。

  2. TTY驱动程序具有“前台过程组”的概念。

  3. 当您按Ctrl+时C,TTY将发送SIGINT到“前台流程”组中的每个流程。(另请参见此博客条目。)

这就是为什么编译后的二进制文件启动它的脚本都被破坏的原因。实际上,我只希望主应用程序接收此信号,而不希望启动脚本。

现在解决方案显而易见:我们需要将应用程序放入一个新的流程组中,并使其成为此TTY的前台流程组。显然,执行此操作的命令是

setsid -c <applcation>

仅此而已。现在,当用户按下Ctrl+时C,SIGINT将被发送到应用程序(及其可能包含的所有子级),并且没有其他人。这就是我想要的。

  • setsid 本身会将应用程序放入一个新的进程组(实际上,是一个全新的“会话”,显然是一组进程组)。

  • 添加-c标志将使该新过程组成为当前TTY的“前景”过程组。(即,SIGINTCtrl+ 时得到C

我已经看到了很多有关Bash何时在新进程组中运行或不运行进程的信息。(特别是对于“交互式”和“非交互式” shell来说似乎有所不同。)我已经看到了一些建议,建议您可以将其与巧妙的管道欺骗一起使用 ...我不知道。但是上面的方法似乎对我有用。


5
几乎可以理解...运行脚本时默认情况下禁用作业控制,但是您可以使用启用它set -m。它比setsid每次生孩子时都要使用的清洁和简单。
psusi 2014年

@psusi感谢您的提示!我只需要抚养一个孩子,所以没什么大不了的。我现在知道在Bash手册中应该看哪里...
MathematicalOrchid

具有讽刺意味的是,我有一个相反的问题,即我希望父母抓住Sigint,但这不是因为“聪明的管道骗术”。进度组->处理组
Andrew Domaszek 2015年

2

正如我在f01的注释中提到的,您应该将SIGTERM发送到子进程。以下是一些脚本,这些脚本显示了如何捕获^ C并将信号发送到子进程。

首先,父母。

陷阱测试

#!/bin/bash

# trap test
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
child=sleeploop

set_trap()
{
    sig=$1
    msg="echo -e \"\n$myname received ^C, sending $sig to $child, $pid\""
    trap "$msg; kill -s $sig $pid" SIGINT
}
trap "echo \"bye from $myname\"" EXIT

echo "running $child..."
./$child 5  &
pid=$!

# set_trap SIGINT
set_trap SIGTERM
echo "$child pid = $pid"

wait $pid
echo "$myname finished waiting"

现在,孩子。

睡眠循环

#!/bin/bash

# child script for traptest
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
delay="$1"

set_trap()
{
    sig=$1
    trap "echo -e '\n$myname received $sig signal';exit 0" $sig
}

trap "echo \"bye from $myname\"" EXIT
set_trap SIGTERM
set_trap SIGINT

#Select sleep mode
if false
then
    echo "Using foreground sleep"
    Sleep()
    {
        sleep $delay
    }
else
    echo "Using background sleep"
    Sleep()
    {
        sleep "$delay" &
        wait $!
    }
fi

#Time to snooze :)
for ((i=0; i<5; i++));
do
    echo "$i: sleeping for $delay"
    Sleep
done

echo "$myname terminated normally"

如果traptest发送SIGTERM,则一切正常,但是如果traptest发送SIGINT,则sleeploop永远不会看到它。

如果sleeploop捕获SIGTERM,并且睡眠模式为前台,那么它只有在从当前睡眠中唤醒后才能响应该信号。但是,如果睡眠模式是背景,它将立即响应。


谢谢您提供的出色示例,它帮助我理解了如何改善脚本的很多方法:)
TabeaKischka

2

在启动bash脚本中。

  • 跟踪第二个程序的PID

  • 赶上SIGINT

  • 捕获到SIGINT后,将SIGINT发送到第二个程序PID


1
那可能无济于事。如果您将SIGINT捕获在父脚本中,则子脚本将无法接收SIGINT,无论您是从父脚本还是从其他Shell发送它。但这并没有听起来那么糟糕,因为您可以将SIGTERM发送给孩子,无论如何,这显然是首选的信号。
下午14年

1
为什么SIGTERM是“首选”?
MathematicalOrchid

它们几乎相似。SIGINT是控制终端/用户发送的信号,例如Ctrl + C。如果要终止进程,也可以发送SIGTERM。更多想法,请参见en.wikipedia.org/wiki/Unix_signal
f01 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.