#!/bin/bash
while true
do
ping 127.0.0.1
done
ping.sh
$> ping.sh
^c
bash script can't stop
首先要明确的是:
前台进程组受到sigint 后,一般情况下会立即退出。 因为用户键入^+c的本意是终止前台进程组。
但是, ping在受到 sigint后, 立即capture 该信号, 也就是不执行默认动作。而是 通过 exit 正常退出, 发出sigchld 信号给父进程(bash脚本), 父进程认为该自进程正常结束, 循环继续。
源代码分析:
bash 4.3.3
jobs.c
当用户键入ctrl+c后, bash脚本和其子进程 都看见sigint信号,(他们是前台进程组成员)。 bash脚本作为父进程处于waitpid blocking状态, 收到sigint 后waitpid 立即返回 -1, 设置errorno为 EINTR。同时 bash要求child 立即死亡(也就是sigint的默认动作 terminate)。最后设置 child_caught_sigint = 1, bash 随后会根据该flag 退出;
如果child capture signal,设置 child_caught_sigint = 0; bash 根据该flag 不退出, 因为子进程capture sigint后, 并立即退出。 其信号处理函数需要做特定处理, 然后通过exit退出, 父进程会受到 sigchld 信号,父进程会认为子进程正常退出, 循环继续。
/* If waitpid returns -1/EINTR and the shell saw a SIGINT, then we
assume the child has blocked or handled SIGINT. In that case, we
require the child to actually die due to SIGINT to act on the
SIGINT we received; otherwise we assume the child handled it and
let it go. */
if (pid < 0 && errno == EINTR && wait_sigint_received)
child_caught_sigint = 1;
if (pid <= 0)
continue; /* jumps right to the test */
/* If the child process did die due to SIGINT, forget our assumption
that it caught or otherwise handled it. */
if (WIFSIGNALED (status) && WTERMSIG (status) == SIGINT)
child_caught_sigint = 0;
(
bash 能够判断子进程是否因为 sigint 而自然terminate
/* If the child process did die due to SIGINT, forget our assumption
that it caught or otherwise handled it. */
if (WIFSIGNALED (status) && WTERMSIG (status) == SIGINT)
child_caught_sigint = 0;
)
如果child capture sigint 则调用 trap handler,
if (JOBSTATE (job) == JDEAD)
{
/* If we're running a shell script and we get a SIGINT with a
SIGINT trap handler, but the foreground job handles it and
does not exit due to SIGINT, run the trap handler but do not
otherwise act as if we got the interrupt. */
if (wait_sigint_received && interactive_shell == 0 &&
child_caught_sigint && IS_FOREGROUND (job) &&
signal_is_trapped (SIGINT))
{
int old_frozen;
wait_sigint_received = 0;
last_command_exit_value = process_exit_status (child->status);
old_frozen = jobs_list_frozen;
jobs_list_frozen = 1;
tstatus = maybe_call_trap_handler (SIGINT);
jobs_list_frozen = old_frozen;
}
至于更多的细节请补充
阅读(2071) | 评论(0) | 转发(0) |