What Exactly Sig_Dfl Do

What exactly SIG_DFL do?

What exactly SIG_DFL (defaut handler for signals) do?

It does exactly what one would expect: informs the kernel that there is no user signal handler for the given signal, and that the kernel should take default action for it (the action itself may be to ignore the signal, to terminate the program (with or without core dump), etc. depending on the signal).

Where is SIG_DFL source code? Libc?

There is usually a #define SIG_DLF -1 in /usr/include/signal.h, but the decision on what to do is in the kernel.

What does signal(SIGPIPE, SIG_IGN); do?

signal(SIGPIPE, SIG_IGN);

simply ignores the signal SIGPIPE. Passing SIG_IGN as handler ignores a given signal (except the signals SIGKILL and SIGSTOP which can't caught or ignored).

As an aside, it's generally recommended to use sigaction(2) over signal(2) as sigaction offers better control and also don't need to "reset" the handler on some systems (which follow the System V signal behaviour).

What does signal() do?

On *nix, signals are callbacks from the kernel to userspace. They are somewhat similar to interrupts, which can be thought of as callbacks from the hardware to the operating system.

They are more alike events in event-based systems than signals/slots in Qt. In Qt, a component can emit signals, which can be received by several slots in other components, and the program sets the connections between signals and slots, In other systems (Xlib, Win32), the program receives predetermined events from the system.

*nix supports blocking and non-blocking sockets. The poll() and select() system calls allow a program to wait for an I/O event. As an alternative to poll()/select(), BSD and Linux can also be made to send a signal (SIGIO by default) when an I/O event occurs, achieving a form of asynchronous I/O. More information can be found in the manpage for fcntl(), looking for F_SETFL, F_SETOWN, F_SETSIG.

Do I need to do anything with a SIGCHLD handler if I am just using wait() to wait for 1 child to finish at a time?

Theory

POSIX says about SIG_IGN (strictly under the XSI extension note):

If the action for the SIGCHLD signal is set to SIG_IGN, child processes of the calling processes shall not be transformed into zombie processes when they terminate. If the calling process subsequently waits for its children, and the process has no unwaited-for children that were transformed into zombie processes, it shall block until all of its children terminate, and wait(), waitid(), and waitpid() shall fail and set errno to [ECHILD].

And in the description of <signal.h> says that the default signal disposition for SIGCHLD is SIG_IGN 'ignore' (code 'I'). However, it is the SIG_DFL behaviour is to 'ignore' the signal; the signal is never generated. This is different from the SIG_IGN behaviour.

So, you don't have to set a signal handler, but you shouldn't get information about the process back — you should just get an error once there are no children left.

Demonstration Code

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static siginfo_t sig_info;
static volatile sig_atomic_t sig_num;
static void *sig_ctxt;

static void catcher(int signum, siginfo_t *info, void *vp)
{
sig_num = signum;
sig_info = *info;
sig_ctxt = vp;
}

static void set_handler(int signum)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = catcher;
sigemptyset(&sa.sa_mask);

if (sigaction(signum, &sa, 0) != 0)
{
int errnum = errno;
fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));
exit(1);
}
}

static void prt_interrupt(FILE *fp)
{
if (sig_num != 0)
{
fprintf(fp, "Signal %d from PID %d\n", sig_info.si_signo, (int)sig_info.si_pid);
sig_num = 0;
}
}

static void five_kids(void)
{
for (int i = 0; i < 5; i++)
{
pid_t pid = fork();
if (pid < 0)
break;
else if (pid == 0)
{
printf("PID %d - exiting with status %d\n", (int)getpid(), i);
exit(i);
}
else
{
int status = 0;
pid_t corpse = wait(&status);
printf("Child: %d; Corpse: %d; Status = 0x%.4X\n", pid, corpse, (status & 0xFFFF));
prt_interrupt(stdout);
fflush(0);
}
}
}

int main(void)
{
printf("SIGCHLD set to SIG_IGN\n");
signal(SIGCHLD, SIG_IGN);
five_kids();
printf("SIGCHLD set to catcher()\n");
set_handler(SIGCHLD);
five_kids();
return(0);
}

The fflush(0); call ensures standard output (in particular) is flushed, which matters if the output of the sample program is piped to another program.

Example Output

The output from the example code agrees with the theory — but requires a little interpretation.

SIGCHLD set to SIG_IGN
PID 4186 - exiting with status 0
SIGCHLD set to SIG_IGN
Child: 4186; Corpse: -1; Status = 0x0000
PID 4187 - exiting with status 1
Child: 4187; Corpse: -1; Status = 0x0000
PID 4188 - exiting with status 2
Child: 4188; Corpse: -1; Status = 0x0000
PID 4189 - exiting with status 3
Child: 4189; Corpse: -1; Status = 0x0000
PID 4190 - exiting with status 4
Child: 4190; Corpse: -1; Status = 0x0000
SIGCHLD set to catcher()
PID 4191 - exiting with status 0
SIGCHLD set to catcher()
Child: 4191; Corpse: -1; Status = 0x0000
Signal 20 from PID 4191
Child: 4192; Corpse: 4191; Status = 0x0000
PID 4192 - exiting with status 1
Child: 4193; Corpse: 4192; Status = 0x0100
Signal 20 from PID 4192
PID 4193 - exiting with status 2
Child: 4194; Corpse: 4193; Status = 0x0200
Signal 20 from PID 4193
PID 4194 - exiting with status 3
Child: 4195; Corpse: 4194; Status = 0x0300
Signal 20 from PID 4194
PID 4195 - exiting with status 4

The first section of the output agrees exactly with the theory; the calling code gets no information about the dead children except that there are no dead children (left) to wait for.

The second section of the output agrees with the theory too, but it appears that the children aren't executing before the parent, so the first wait() from the parent has no dead children to wait for, and it returns -1. Subsequent calls get the various children, but one escapes unwaited for (but the corpse will be cleaned up by the system). If the code used waitpid() such as: pid_t corpse = waitpid(pid, &status, 0);, it would wait for each child in turn.

Some of this commentary may need revision in the light of the modified comments about 'ignore' above. TBD — out of time right now.

Mac OS X 10.8.4 on 3 GHz Intel Core 2 Duo, GCC 4.6.0, 64-bit compilation:

gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition sigchld.c -o sigchld

This was a quick adaptation of some existing code to test the behaviour of SIGINT and pause(). It may have some superfluous material in it. You do not have to call wait() or one of its relatives in the signal handler; you may do so if you wish, though.

How to restore original signal handling properties in C

Thank you everyone who contributed to this question. The resources provided/linked were tremendously helpful in learning more about signals (and that EOF isn't a signal), among the other wealth of information provided.

After some more research, I found out that somehow, either through some accidental bash command gone awry, or perhaps the program posted in my original question itself, I had altered the key mappings for my terminal's stty settings. If anyone finds themselves in this oddly specific situation in the future, I hope this can be of help, as it is what fixed my problem:

Enter the command $ stty -a to see all of your terminals settings, specifically the "cchars" section.

I then saw the reversal, and fixed it like so:

$ stty intr ^C
$ stty eof ^D

Then you can run $ stty -a once again to see that the changes have properly taken effect. Once again, thanks everyone.

How do I trigger the default signal handling behavior?

Credit for originally noticing this goes to RealSkeptic, but I wanted to expand on it in an answer.

The default behavior for SIGINT, SIGTERM, and SIGHUP is not, in fact, SignalHandler.SIG_DFL. Instead, the java.lang.Terminator class registers a SignalHandler that simply calls Shutdown.exit():

SignalHandler sh = new SignalHandler() {
public void handle(Signal sig) {
Shutdown.exit(sig.getNumber() + 0200);
}
};

You can capture this SignalHandler by calling Signal.handle() (since it returns the old handler), or you can simply define your own handler that calls System.exit() which will do the same thing.

Note that Terminator's call to Shutdown.exit() is not exactly the same as System.exit(). The former is package-private, meaning you can't call it directly. If a security manager prevents you from calling System.exit(), you'll have to capture the original handler and reuse it.

Warning: this is undocumented behavior. It's unlikely but entirely possible that future releases of Java could change this behavior.

What's the difference between various $SIG{CHLD} values?

See %SIG.

$SIG{CHLD} = 'IGNORE'; causes your process to ignore SIGCHLD signals.

$SIG{CHLD} = 'DEFAULT'; causes your process to treat SIGCHLD signals as it would had you not messed with $SIG{CHLD} or equivalent. According to kill(1), a process on my system ignores SIGCHLD by default

$SIG{CHLD} = ''; and $SIG{CHLD} = undef; are not valid values.

As for reaping, children of a parent whose SIGCHLD handler is explicitely set to IGNORE will be reaped automatically by the system as soon as it exits.

$ perl -e'
my $pid = fork;
if (!$pid) { sleep(1); exit; }
sleep(2);
system "ps -o pid,stat,command $pid";
'
PID STAT COMMAND
14667 ZN+ [perl] <defunct>

$ perl -e'
$SIG{CHLD}="IGNORE";
my $pid = fork;
if (!$pid) { sleep(1); exit; }
sleep(2);
system "ps -o pid,stat,command $pid";
'
PID STAT COMMAND

$

SIGSEGV does not terminate the process

The situation was not as straightforward as I thought. The original problem was a deadlock between two of the threads. When I issued SIGTERM, then actually my custom signal handler caused a segfault in the (now un-)deadlocked threads.

GDB, signals and SIG_IGN

You should look into the handle gdb command:

(gdb) handle SIGALRM
Signal Stop Print Pass to program Description
SIGALRM No No Yes Alarm clock
(gdb) handle SIGALRM ignore
Signal Stop Print Pass to program Description
SIGALRM No No No Alarm clock
(gdb) help handle
... read it ;-) ...

As mentioned in the manpage of ptrace(2) (which gdb is using):

While being traced, the tracee will stop each time a signal is delivered, even if the signal is being ignored.



Related Topics



Leave a reply



Submit