All about Linux signals

Handling SIGCHLD


If you create new processes in your program and don't really want to wait until they exit, and possibly their exist status doesn't matter, just want to cleanup zombie processes you can create a SIGCHLD handler that does just that and forget about process you've created. This handler can look like this one:

Fragment of example: SIGCHLD handler

  1. static void sigchld_hdl (int sig)
  2. {
  3. /* Wait for all dead processes.
  4. * We use a non-blocking call to be sure this signal handler will not
  5. * block if a child was cleaned up in another part of the program. */
  6. while (waitpid(-1, NULL, WNOHANG) > 0) {
  7. }
  8. }

This way whenever a child exits it will be cleaned-up but information which process was that, why it exited and its exit status is forgotten. You could make the handler more intelligent but remember to not use any function that is not listed as signal-safe.

You must remember that if you make child processes SIGCHLD must have a handler. The behavior of ignoring this signal is undefined, so at least a handler that doesn't do anything is required.

Handling SIGBUS


The SIGBUS signal is sent to the process when you access mapped memory (with mmap(2)) that doesn't correspond to a file. A common example is that the file you've mapped was later truncated (possible by another program) and you read past it's current end. Accessing files this way doesn't require any system function that could return an error, you just read from memory like if it was on the heap or stack. This is a really bad situation when you don't want your program to terminate after a file read error. Unfortunately handling SIGBUS isn't simple or clean, but it's possible. If you want to continue running your program you have to use longjmp(3). It's something like goto but worse! We have to jump to some other place in the program that the mmap()ed memory is not accessed if we receive SIGBUS. If you place an empty handler for this signal, in case of read error the program will be interrupted, signal handler executed and the control returns to the same place that caused the error. So we need to jump into another place from the signal handler. This sounds low-level, but it's possible using standard POSIX functions.

See the example: SIGBUS handling

You must keep in mind the list of signal-safe functions: In this example we never actually return from the signal handler. The stack is cleaned up, but program is restarted in completely different place, so if you've had, for example, a mutex locked during the operation like:

  1. pthread_mutex_lock (&m);
  2. for (l = 0; l < 1000; l++)
  3. if (mem[l] == 'd') // BANG! SIGBUS here!
  4. j++;
  5. pthread_mutex_unlock (&m);

After longjmp(3) the mutex is still held although in every other situation the mutex is released.

So handling SIGBUS is possible but very tricky and can introduce bugs that are very hard to debug. The program's code also becomes ugly.

Handling SIGSEGV


Handling the SIGSEGV (segmentation fault) signal is also possible. In most cases returning from the signal handler makes no sense since the program will be restarted from the instruction that caused segmentation fault. So if you have no solution on how to fix the state of the program to let it continue running properly at the same moment it crashed, you must end the program. One example of when you may restart the program is when you have memory obtained using mmap(2) that is read-only, you may check if the signal handler that the cause of segmentation fault was writing to this memory (using data from siginfo_t) and use mprotect(2) to change the protection of this memory. How practical is it? I don't know.

Exhausting stack space is one of the causes of segmentation fault. In this case running a signal handler is not possible because it requires space on the stack. To allow handling SIGSEGV in such condition the sigaltstack(2) function exists that sets alternative stack to be used by signal handlers.

Handling SIGABRT


When handling this signal you should keep in mind how the abort(3) function works: it rises the signal twice, but the second time the SIGABRT handler is restored to the default state, so the program terminates even if you have a handler defined. So you actually have a chance to do something in case of abort(3) before the program termination. It's possible to not terminate the program by not exiting from the signal handler and using longjmp(3) instead as described earlier.

Comments

Hello, Thank you for good

Hello, Thank you for good article. Is it possible to give permission of other user program to send signal to my process ? If yes, then how to do this ?

I don't think it's possible.

I don't think it's possible.

Restarting system calls

"It's not specified which calls are restarted" - says the last paragraph. It is very much specified, on the signal(7) man page.

very useful and readable blog about Unix signals

Unix signals is a deep and interesting topic. For example, the signal() function is either referring to the version in the C library or the version in the operating system. The topic of signals has a rich history as part of Unix.

Thanks

I just want to say thank you , I was googling about what happens to signals handlers when I fork() and you blog showed up. thanks

Ctrl+Z Signal

CTRL-Z - sends SIGTSTP

Hi, typo jerk here

Hi, typo jerk here again: page3: Moreover, it lack's features -> lacks why the signal was send -> sent page5: signals like SIGPIPE, SIGUSR1, SIGUSR1 -> SIGUSR2 signal i exits -> it This program read from it's -> reads, its Additionally when SIGUSR1 -> Additionally, when [missing comma] I hope you really, truly don't consider this as some kind of personal attack.

i love type jerk!

typos suck! combat them!

Thanks. I don't consider it

Thanks. I don't consider it as an attack :) Some of them are just caused by the fact that English is not my native language.

I was looking for linux

I was looking for linux programming tutorial and i found this blog . keep good work.

Great write-up

Great write-up; thanks for the information.

Signals - That's not everything

I wouls suggest the reading of "Advanced Programming in the Unix Environment". It does not address signals with threads, but it is the most extensive explanation of signal handling. It treats also long jumps to remove races in signal handling.

Problem regarding signals.

Hi I have written a sample program to understand signal handling. signal1.c
#include 
#include 
/* for random() stuff */
#include 
#include 
#include 
#include 

void
termination_handler (int signum)
{
  struct temp_file *p;
 int err;
  printf("\nTerminated\n");
		sleep(10); 
}

int
main (void)
{

	int shmfd,*shared_msg, pid;
	struct sigaction new_action, old_action;
	int shared_seg_size = (1 * sizeof(int));
	//shm operations
	shmfd = shm_open("/shm_sumit",O_CREAT|O_RDWR|O_EXCL,S_IRWXU | S_IRWXG);
	if (shmfd < 0) {
	        perror("In shm_open()");
        	exit(1);
    	}

	ftruncate(shmfd, shared_seg_size);
	shared_msg = (int *)mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
	if (shared_msg == NULL) {
	        perror("In mmap()");
        	exit(1);
    	}

        pid = getpid();
        *shared_msg = pid; 

  /* Set up the structure to specify the new action. */
  new_action.sa_handler = termination_handler;
  sigemptyset (&new_action.sa_mask);
  new_action.sa_flags = SA_NODEFER;

    sigaction (SIGUSR1, &new_action, NULL);
     //while(1)
{
   sleep(10);
}
	/*if (shm_unlink("/shm_sumit") != 0) {
	        perror("In shm_unlink()");
        	exit(1);
    	}*/

}



signal2.c


#include 
#include 
#include 
void
termination_handler (int signum)
{
  struct temp_file *p;
  printf("\nTerminated");
}

int
main (void)
{
	int shmfd,*shared_msg, pid;
    struct sigaction new_action, old_action;
    int shared_seg_size = (1 * sizeof(int));
    //shm operations
    shmfd = shm_open("/shm_sumit",O_RDWR,S_IRWXU | S_IRWXG);
    shared_msg = (int *)mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
    printf(" Process id of sigtest1 = %d\n",*shared_msg);
 kill(*shared_msg,SIGUSR1);
 kill(*shared_msg,SIGUSR1);
 kill(*shared_msg,SIGUSR1);
 kill(*shared_msg,SIGUSR1);
 kill(*shared_msg,SIGUSR1);
	
	sleep(1);
	if (shm_unlink("/shm_sumit") != 0) {
        perror("In shm_unlink()");
        exit(1);
    }

}
here sigtest2.c send SIGUSR1 to sigtest1.c 5 times, but the string "Terminated " gets printed sometimes 2 times and sometimes 5 times. why is this printing behavior inconsistent??

Hi, because while handling

Hi, because while handling the signal it wont be handle the signal of same type . you can check this by remove the sleep(10) in sigtest1.c . It will work fine.