On Tue, Apr 25, 2017 at 03:21:24PM +0200, Vincent Lefevre wrote:
> I wonder how the following script is supposed to behave.

> The POSIX spec seems ambiguous about SIGCHLD trap in a shell. It says:
> "[...] a signal specified using a symbolic name, without the SIG prefix,
> as listed in the tables of signal names in the <signal.h> header defined
> in XBD Headers"; there, SIGCHLD is listed as "Child process terminated,
> stopped, [XSI] or continued". From this spec, I would assume that the
> trap would be executed for any child process that terminates, stops, or
> continues. But this is not what I observe.

I would not assume that. Since trap actions are not executed while
waiting for a foreground process to exit, it is likely that multiple
instances of SIGCHLD will be coalesced into a single trap action

Also, SIGCHLD instances may be lost if waitpid() or similar calls
implicitly accept them. This is most likely if the shell calls waitpid()
with SIGCHLD masked but might also happen in a race otherwise.

> Only bash, ksh93 and zsh document what happens concerning SIGCHLD traps.

> Note: For practical reasons, I assume a sleep implementation with
> a floating-point number; this is the case with /bin/sleep under
> Debian (coreutils). It could be any external command with a similar
> behavior.

> ------------------------------------------------------------------------
> #!/bin/sh
> n=${1:-30}
> n=$((n+0))
> c=0
> trap 'c=$((c+1))' CHLD
> for i in `seq $n`; do /bin/sleep 0.1 & done
> while true
> do
>   echo $c
>   /bin/sleep 0.1
> done
> ------------------------------------------------------------------------

> I call this script with:

>   <shell> <script_name>

> Under Debian unstable:

> * With ash, dash, mksh and yash, I get:

> 1
> 2
> 3
> 4
> 5
> 6
> [...]

> and each time, $c is increased by 1.

> The +1 comes from the "/bin/sleep 0.1" of the "while" loop ("true"
> and "echo" being built-ins). If I replace them by external commands
> (/bin/true and /bin/echo), I get +3 each time.

> So, I get +1 only for commands that are not executed in background
> (asynchronously).

You can get the SIGCHLD trap action executed for background jobs by
doing slow things that do not fork, such as invoking the read utility.

FreeBSD sh behaves like this as well, with the addition of a check that
prevents nested SIGCHLD traps.

> * With bash and posh, I get:

> 0
> 0
> 0
> 0
> 0
> 0
> [...]

> i.e. no trap executions at all.

> If I add "set -m", then bash behaves like ash, etc., except that it
> no longer reacts to Ctrl-C (the Ctrl-C only interrupts the sleep).
> Anyway, "set -m" is not supposed to be used in shell scripts (see
> rationale for the "set" built-in).

"set -m" can be used usefully in scripts to put jobs into their own
process groups, like
  set -m; something & set +m

This example turns -m mode off immediately, since handling suspension of
foreground jobs is usually undesirable in a script.

Whether this works well depends on the shell implementation.

In any case it seems strange to tie SIGCHLD behaviour to "set -m".

> * With ksh93 and zsh, I get:

> 0
> 30
> 30
> 30
> 30
> 30
> [...]

> (or with a 29 just after the 0 -- this is just a timing reason),
> i.e. a trap execution only for asynchronous lists.

> This corresponds to the behavior of ksh88 as mentioned in the
> rationale for the "set" built-in. Is this the original intent?
> In any case, I think that this is the most useful behavior.

That seems more useful, but there is no text in POSIX that says to
implement it that way.

Jilles Tjoelker

Reply via email to