Process library and signals

Glynn Clements glynn at gclements.plus.com
Wed Feb 2 09:39:06 EST 2005


Simon Marlow wrote:

> I've now fixed system and rawSystem to do something more appropriate on
> POSIX systems: they now disable SIGINT and SIGQUIT in the parent, and
> reset these signals to SIG_DFL in the child.  This isn't completely
> correct, but it's better than before.

Just a reminder, as the original discussion was back in October:

The POSIX system() behaviour is to ignore the signals in the parent
(before the fork()) and "un-ignore" them in the child, i.e. restore
whichever handlers where in effect upon entry to system(), before the
parent ignored them. E.g.:

	sighandler_t old_quit = signal(SIGQUIT, SIG_IGN);
	sighandler_t old_intr = signal(SIGINT , SIG_IGN);

	pid_t pid = fork();

	if (pid == 0) {		/* child */
		signal(SIGQUIT, old_quit);
		signal(SIGINT , old_intr);
		execve(...);
	}
	else if (pid > 0) {	/* parent */
		waitpid(...);
	}

	signal(SIGQUIT, old_quit);
	signal(SIGINT , old_intr);

The effect is almost the same as ignoring them in the parent *after*
the fork(), but that has a potential race condition, i.e. if the
signal arrived after the fork() but before the parent ignored it. 
Ignoring before the fork() and restoring in the child eliminates that
possibility.

Any actual signal handlers (i.e. pointers to actual functions rather
than SIG_IGN or SIG_DFL) will be reset to SIG_DFL by execve(), as the
handler functions won't exist in the new program. But if the signal
was already being ignored on entry to system(), it will continue to be
ignored in the child (this is the key difference).

Also, the POSIX system() function blocks SIGCHLD in the parent, but I
don't know whether that's appropriate in GHC. The reason is to prevent
interactions between existing SIGCHLD handlers and the child process
which system() creates (e.g. an existing SIGCHLD handler might call
wait() and accidentally reap system()'s child).

> I think this covers most of the useful situations.  If you want to do
> the same thing in both parent and child, or handle in the parent and
> SIG_DFL in the child: use runProcess.  If you want to ignore in the
> parent and SIG_DFL in the child: use System.Cmd.{system,rawSystem}.  To
> handle in the parent and ignore in the child: unfortunately not directly
> supported.

As it stands, you can have whatever behaviour you want in the parent:
set the desired handling before calling system/rawSystem/runProcess
then set it back afterwards.

However, this will cease to be true for system/rawSystem if you change
them so that the child restores the handlers to their state upon
entry.

> I realise this doesn't address the library design issues you raised, but
> as you pointed out there doesn't seem to be a good platform-independent
> solution here.

Yes; any programs which perform non-trivial process management will
have to use platform-specific functionality.

-- 
Glynn Clements <glynn at gclements.plus.com>


More information about the Glasgow-haskell-users mailing list