1.

1.

要了解

如果 Bash 正在等待命令完成并接收到已设置陷阱的信号,则在命令完成之前不会执行陷阱。

当 Bash 通过 wait 内置函数等待异步命令时,接收到已设置陷阱的信号将导致内置 wait 立即返回,退出状态大于 128,然后立即执行陷阱。

根据 Bash 手册,我运行以下命令:

  1. 在我的两个示例中,SIGINT(使用 Ctrl-C 发送)立即终止前台作业(引用中的第一个案例)和后台作业(引用中的第二个案例),而不等待它们完成。

    引用中的第一句话是否意味着如果 Bash 正在运行前台作业并接收 signal SIGINT,则 signal 的陷阱 SIGINT在设置后将被执行,直到命令完成?如果是,为什么在我的第一个示例中,ctrl-C使前台作业在完成之前立即存在?

    $ sleep 10000  # a foreground job
    ^C
    
    
    $ sleep 10000 & # a background job
    [1] 21219
    $ wait 21219
    ^C
    $ echo $?
    130
    
  2. 什么是“已设置陷阱的信号” 意思是,

    • arg已通过 指定陷阱的信号trap arg sigspec,或

    • 一个未被忽略的信号,或者

    • 一个其陷阱不是默认陷阱的信号?

    在第 1 部分的示例中,我没有为 SIGINT 设置陷阱,因此信号有其默认处理程序(它会中断任何执行循环)。是一个 具有默认处理程序的信号算不算已经设置了陷阱?

  3. 我设下陷阱SIGINT,但ctrl-C会使以下命令在完成之前退出。那么这是否与我引用的第一句话相反?

    $ trap "echo You hit control-C!" INT
    $ bash -c 'sleep 10; echo "$?"'
    ^C
    $
    

    在设置陷阱之前SIGINTctrl-C也会使相同的命令在完成之前退出。那么这是否与我引用的第一句话相反?

    $ bash -c 'sleep 10; echo "$?"'
    ^C
    
  4. 你能举一些例子来解释一下引用中的两句话的意思吗?

谢谢。

答案1

“已设置陷阱的信号”是什么意思?

这是一个已定义处理程序的信号(使用trap 'handling code' SIG),其中处理代码不为空,因为这会导致信号被忽略。

因此,具有默认配置的信号不是已设置陷阱的信号。您帖子中的一些引用也适用于具有默认配置的信号,尽管显然不是关于运行陷阱,因为没有为它们定义陷阱。


该手册讨论了信号传递到外壳,而不是您从该 shell 运行的命令。

1.

如果 Bash 正在等待命令完成并接收到已设置陷阱的信号,则在命令完成之前不会执行陷阱。

(1)

为什么在我的第一个示例中,ctrl-C 会使前台作业在完成之前立即退出

如果您sleep 10在交互式 shell 的提示符下运行,shell 会将该作业放入前景(通过ioctl()tty 设备上的一个告诉终端线路规则哪个进程组是前台进程组),所以只会sleep收到 SIGINT ^C,而交互式 shell 则不会,因此测试该行为没有用。

  • 父 shell,因为它是交互式的,不会收到 SIGINT,因为它的进程不在前台进程组中。

  • 每个命令都可以随意处理信号。sleep不会对 SIGINT 执行任何特殊操作,因此将获得默认配置(终止),除非在启动时忽略 SIGINT。

(2)如果您sleep 10在非交互式 shell 中运行,

bash -c 'sleep 10; echo "$?"'

当您按 Ctrl-C 时,非交互式bashshell 和sleep都会收到 SIGINT。

如果bash立即退出,sleep如果碰巧忽略或处理 SIGINT 信号,则可能会使命令在后台无人值守地运行。所以与其,

  • bash像大多数其他贝壳一样,等待命令时接收信号(至少是一些信号)。
  • 命令退出后将恢复传送(执行陷阱)。这也避免了陷阱中的命令与其他命令同时运行。

在上面的例子中,sleep将在 SIGINT 时死亡,所以bash不需要很长时间来处理它自己的 SIGINT(这里死亡,因为我没有添加trapon SIGINT)。

(3) 当您在运行非交互式 shell 时按 Ctrl+C:

bash -c 'sh -c "trap \"\" INT; sleep 3"; echo "$?"'

(没有trapSIGINT)bash不会被 SIGINT 杀死。bash,就像其他一些 shell 专门对待 SIGINT 和 SIGQUIT 一样。他们实施等待并配合退出行为描述于https://www.cons.org/cracauer/sigint.html(众所周知会引起一些烦恼,例如调用 SIGINT 处理命令的脚本不能被中断^C

(4) 为了正确测试,您应该运行非交互式bash已设置 SIGINT 陷阱并调用SIGINT 后不会立即终止的命令喜欢:

bash -c 'trap "echo Ouch" INT; sh -c "trap \"\" INT; sleep 3"'

bash正在等待sh(以及sleep) SIGINT 被忽略(因为trap "" INT),所以 SIGINT 不会杀死sleepshbash不忽略 SIGINT,但其处理被推迟到sh返回。您看到的Ouch显示不是在 上Ctrl+C,而是在sleepsh正常终止之后。

请注意,该trap命令为其运行的同一 shell 设置了一个信号陷阱。因此,当trap命令在非交互式 shell 之外和父 shell 中执行时,

$ trap "echo You hit control-C!" INT
$ bash -c 'sleep 10; echo "$?"'
^C
$

非交互式bash, 和sleep命令不会trap从父 shell 继承它。执行不同的命令时信号处理程序会丢失(execve()擦除进程的整个地址空间,包括处理程序的代码)。一旦execve(),定义了处理程序的信号恢复为默认配置,那些被忽略的信号仍然被忽略。除此之外,在大多数 shell 中,traps 也会在子 shell 中重置。

2.

当 Bash 通过 wait 内置函数等待异步命令时,接收到已设置陷阱的信号将导致内置 wait 立即返回,退出状态大于 128,然后立即执行陷阱。

wait明确使用时wait 会被任何设置了陷阱的信号中断(显然还有那些完全杀死 shell 的信号)。

这使得很难当存在捕获信号时可靠地获取命令的退出状态

$ bash -c 'trap "echo Ouch" INT; sh -c "trap \"\" INT; sleep 10" & wait "$!"; echo "$?"'
^COuch
130

在这种情况下,sleepsh没有被 SIGINT 杀死(因为他们忽略了它)。仍然wait返回130退出状态,因为在等待时收到了信号 (SIGINT) sh。您需要重复wait "$!"直到sh真正终止才能获取sh退出状态。

相关内容