Chapter 21. Signals

Introduction

Signals are a notification mechanism among processes, they can be either termination or warning signals, that allow process coordination and monitoring

Objectives

  • Explain what signals are and how they are used
  • Know the different signals and types of signals available in Linux
  • Use kill, killall and pkill to send signals from the command line

What signals are

They are a inter process communication (IPC) mechanism used to notify process about asynchronous events (or exceptions).

The process that receives the signal may

  • Not expect the event to occur
  • Expect the event, but not know when it is most likely to occur

For example if a user decides to terminate a program, it could send a termination signal to the process through the kernel in order to kill the process.

There are two paths by which signals are sent to a process

  • From the kernel to the process [for an exception or programming error]
  • From the process to the kernel

Signals can only be sent between processes owned by the same user or from a process owned by the superuser to any process.

When a process receives a signal it will handle such signal in accordance to its code, however there are two types of signals that can not be handled and will always terminate the program

  • SIGKILL
  • SIGSTOP

Types of signals

There are several types of signals, generally signals handle two things

  • Exceptions detected by hardware [such as an illegal memory reference]
  • Exceptions generated by the environment [such as the premature death of a process from the user's terminal]

To see a list of the signals in Linux, along with their numbers do :

​$ kill -l
 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX    

Kill

A process cannot send a signal directly to other process, it myst ask the kernel to send the signal by executing a system call. Users (including the superuser) can send signals to other processes from the command line or scripts by using kill command

Sample

By default if no parameter is specified the signal type is SIGTERM (15), this will not termintate the process allowing the process to perform the close operations needed to terminate.

$ kill 1991

If the -9 parameter is specified, the process will terminate immediately

$ kill -9 1991

Other way to express the -9 paramenter is by using SIGKILL 

$ kill -SIGKILL 1991

The name kill is not really apropiated since this command can send any type of signals to the processes even totally benign informative ones.

killall and pkill

killall command kills all processes with a given name, assuming the user has sufficient privilege. It uses a command name rather than a process ID, and can be done as in

$ killall bash
$ killall -9 bash
$ killall -SIGKILL bash

pkill sends a signal to a process using selection criteria

$ pkill [-signal] [options] [pattern]

for example

$ pkill -u libby foobar

Will kill all libby's process with a name of foobar.

The next command makes rsyslog reread its configuration file

$ pkill -HUP rsyslogd

Lab 21.1: Examining Signal Priorities and Execution

​We give you a C program that includes a signal handler that can handle any signal. The handler avoids making any system calls (such as those that might occur while doing I/O).

You will need to compile it and run it as in:

$ gcc -o signals signals.c
$ ./signals
/*
 * Examining Signal Priorities and Execution.
 *
 * The code herein is: Copyright the Linux Foundation, 2014
 * Author: J. Cooperstein
 *
 * This Copyright is retained for the purpose of protecting free
 * redistribution of source.
 *
 * This code is distributed under Version 2 of the GNU General Public
 * License, which you should have received with the source.
 *
 @*/

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

#define NUMSIGS 64

/* prototypes of locally-defined signal handlers */

void (sig_handler) (int);

int sig_count[NUMSIGS + 1];	/* counter for signals received */
volatile static int line = 0;
volatile int signumbuf[6400], sigcountbuf[6400];

int main(int argc, char *argv[])
{
	sigset_t sigmask_new, sigmask_old;
	struct sigaction sigact, oldact;
	int signum, rc, i;
	pid_t pid;

	pid = getpid();

	/* block all possible signals */
	rc = sigfillset(&sigmask_new);
	rc = sigprocmask(SIG_SETMASK, &sigmask_new, &sigmask_old);

	/* Assign values to members of sigaction structures */
	memset(&sigact, 0, sizeof(struct sigaction));
	sigact.sa_handler = sig_handler;	/* we use a pointer to a handler */
	sigact.sa_flags = 0;	/* no flags */
	/* VERY IMPORTANT */
	sigact.sa_mask = sigmask_new;	/* block signals in the handler itself  */

	/*
	 * Now, use sigaction to create references to local signal
	 * handlers * and raise the signal to myself
	 */

	printf
	    ("\nInstalling signal handler and Raising signal for signal number:\n\n");
	for (signum = 1; signum <= NUMSIGS; signum++) {
		if (signum == SIGKILL || signum == SIGSTOP || signum == 32
		    || signum == 33) {
			printf("  --");
			continue;
		}
		sigaction(signum, &sigact, &oldact);
		/* send the signal 3 times! */
		rc = raise(signum);
		rc = raise(signum);
		rc = raise(signum);
		if (rc) {
			printf("Failed on Signal %d\n", signum);
		} else {
			printf("%4d", signum);
			if (signum % 16 == 0)
				printf("\n");
		}
	}
	fflush(stdout);

	/* restore original mask */
	rc = sigprocmask(SIG_SETMASK, &sigmask_old, NULL);

	printf("\nSignal  Number(Times Processed)\n");
	printf("--------------------------------------------\n");
	for (i = 1; i <= NUMSIGS; i++) {
		printf("%4d:%3d  ", i, sig_count[i]);
		if (i % 8 == 0)
			printf("\n");
	}
	printf("\n");

	printf("\nHistory: Signal  Number(Count Processed)\n");
	printf("--------------------------------------------\n");
	for (i = 0; i < line; i++) {
		if (i % 8 == 0)
			printf("\n");
		printf("%4d(%1d)", signumbuf[i], sigcountbuf[i]);
	}
	printf("\n");
	exit(EXIT_SUCCESS);
}

void sig_handler(int sig)
{
	sig_count[sig]++;
	signumbuf[line] = sig;
	sigcountbuf[line] = sig_count[sig];
	line++;
}

​When run, the program:

  • Does not send the signals SIGKILL or SIGSTOP, which can not be handled and will always terminate a program.
  • Stores the sequence of signals as they come in, and updates a counter array for each signal that indicates how many times the signal has been handled.
  • Begins by suspending processing of all signals and then installs a new set of signal handlers for all signals.
  • Sends every possible signal to itself multiple times and then unblocks signal handling and the queued up signal handlers will be called.
  • Prints out statistics including:
    • The total number of times each signal was received.
    • The order in which the signals were received, noting each time the total number of times that signal had been received up to that point.

Note the following:

  • If more than one of a given signal is raised while the process has blocked it, does the process receive it multiple times? Does the behavior of real time signals differ from normal signals?
  • Are all signals received by the process, or are some handled before they reach it?
  • What order are the signals received in?

One signal, SIGCONT (18 on x86) may not get through; can you figure out why?

Note:

On some Linux distributions signals 32 and 33 can not be blocked and will cause the program to fail. Even though system header files indicate SIGRTMIN=32, the command kill -l indicates SIGRTMIN=34. Note that POSIX says one should use signal names, not numbers, which are allowed to be completely implementation dependent. You should generally avoid sending these signals.