- Memory Mapping
- Signals
- Semaphores
- Message Queues
- Shared Memory
- Named Pipes
- Pipes
- System V & Posix
- Related System Calls (System V)
- Overlaying Process Image
- Other Processes
- Process Resources
- Process Groups, Sessions & Job Control
- Child Process Monitoring
- Process Creation & Termination
- Process Image
- Process Information
- Overview
- Home
Useful Resources
Selected Reading
- Who is Who
- Computer Glossary
- HR Interview Questions
- Effective Resume Writing
- Questions and Answers
- UPSC IAS Exams Notes
Inter Process Communication - Signals
A signal is a notification to a process indicating the occurrence of an event. Signal is also called software interrupt and is not predictable to know its occurrence, hence it is also called an asynchronous event.
Signal can be specified with a number or a name, usually signal names start with SIG. The available signals can be checked with the command kill –l (l for Listing signal names), which is as follows −
Whenever a signal is raised (either programmatically or system generated signal), a default action is performed. What if you don’t want to perform the default action but wish to perform your own actions on receiving the signal? Is this possible for all the signals? Yes, it is possible to handle the signal but not for all the signals. What if you want to ignore the signals, is this possible? Yes, it is possible to ignore the signal. Ignoring the signal imppes neither performing the default action nor handpng the signal. It is possible to ignore or handle almost all the signals. The signals which can’t be either ignored or handled/caught are SIGSTOP and SIGKILL.
In summary, the actions performed for the signals are as follows −
Default Action
Handle the signal
Ignore the signal
As discussed the signal can be handled altering the execution of default action. Signal handpng can be done in either of the two ways i.e., through system calls, signal() and sigaction().
#include <signal.h> typedef void (*sighandler_t) (int); sighandler_t signal(int signum, sighandler_t handler);
The system call signal() would call the registered handler upon generation of signal as mentioned in signum. The handler can be either one of the SIG_IGN (Ignoring the Signal), SIG_DFL (Setting signal back to default mechanism) or user-defined signal handler or function address.
This system call on success returns the address of a function that takes an integer argument and has no return value. This call returns SIG_ERR in case of error.
Though with signal() the respective signal handler as registered by the user can be called, fine tuning such as masking the signals that should be blocked, modifying the behavior of a signal, and other functionapties are not possible. This are possible using sigaction() system call.
#include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)
This system call is used to either examine or change a signal action. If the act is not null, the new action for signal signum is installed from the act. If oldact is not null, the previous action is saved in oldact.
The sigaction structure contains the following fields −
Field 1 − Handler mentioned either in sa_handler or sa_sigaction.
void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *);
The handler for sa_handler specifies the action to be performed based on the signum and with SIG_DFL indicating default action or SIG_IGN to ignore the signal or pointer to a signal handpng function.
The handler for sa_sigaction specifies the signal number as the first argument, pointer to siginfo_t structure as the second argument and pointer to user context (check getcontext() or setcontext() for further details) as the third argument.
The structure siginfo_t contains signal information such as the signal number to be depvered, signal value, process id, real user id of sending process, etc.
Field 2 − Set of signals to be blocked.
int sa_mask;
This variable specifies the mask of signals that should be blocked during the execution of signal handler.
Field 3 − Special flags.
int sa_flags;
This field specifies a set of flags which modify the behavior of the signal.
Field 4 − Restore handler.
void (*sa_restorer) (void);
This system call returns 0 on success and -1 in case of failure.
Let us consider a few sample programs.
First, let us start with a sample program, which generates exception. In this program, we are trying to perform spanide by zero operation, which makes the system generate an exception.
/* signal_fpe.c */ #include<stdio.h> int main() { int result; int v1, v2; v1 = 121; v2 = 0; result = v1/v2; printf("Result of Divide by Zero is %d ", result); return 0; }
Compilation and Execution Steps
Floating point exception (core dumped)
Thus, when we are trying to perform an arithmetic operation, the system has generated a floating point exception with core dump, which is the default action of the signal.
Now, let us modify the code to handle this particular signal using signal() system call.
/* signal_fpe_handler.c */ #include<stdio.h> #include<signal.h> #include<stdpb.h> void handler_spanidebyzero(int signum); int main() { int result; int v1, v2; void (*sigHandlerReturn)(int); sigHandlerReturn = signal(SIGFPE, handler_spanidebyzero); if (sigHandlerReturn == SIG_ERR) { perror("Signal Error: "); return 1; } v1 = 121; v2 = 0; result = v1/v2; printf("Result of Divide by Zero is %d ", result); return 0; } void handler_spanidebyzero(int signum) { if (signum == SIGFPE) { printf("Received SIGFPE, Divide by Zero Exception "); exit (0); } else printf("Received %d Signal ", signum); return; }
Compilation and Execution Steps
Received SIGFPE, Divide by Zero Exception
As discussed, signals are generated by the system (upon performing certain operations such as spanide by zero, etc.) or the user can also generate the signal programmatically. If you want to generate signal programmatically, use the pbrary function raise().
Now, let us take another program to demonstrate handpng and ignoring the signal.
Assume that we have raised a signal using raise(), what happens then? After raising the signal, the execution of the current process is stopped. Then what happens to the stopped process? There can be two scenarios – First, continue the execution whenever required. Second, terminate (with kill command) the process.
To continue the execution of the stopped process, send SIGCONT to that particular process. You can also issue fg (foreground) or bg (background) commands to continue the execution. Here, the commands would only re-start the execution of the last process. If more than one process is stopped, then only the last process is resumed. If you want to resume the previously stopped process, then resume the jobs (using fg/bg) along with job number.
The following program is used to raise the signal SIGSTOP using raise() function. Signal SIGSTOP can also be generated by the user press of CTRL + Z (Control + Z) key. After issuing this signal, the program will stop executing. Send the signal (SIGCONT) to continue the execution.
In the following example, we are resuming the stopped process with command fg.
/* signal_raising.c */ #include<stdio.h> #include<signal.h> #include<stdpb.h> int main() { printf("Testing SIGSTOP "); raise(SIGSTOP); return 0; }
Compilation and Execution Steps
Testing SIGSTOP [1]+ Stopped ./a.out ./a.out
Now, enhance the previous program to continue the execution of the stopped process by issuing SIGCONT from another terminal.
/* signal_stop_continue.c */ #include<stdio.h> #include<signal.h> #include <sys/types.h> #include <unistd.h> void handler_sigtstp(int signum); int main() { pid_t pid; printf("Testing SIGSTOP "); pid = getpid(); printf("Open Another Terminal and issue following command "); printf("kill -SIGCONT %d or kill -CONT %d or kill -18 %d ", pid, pid, pid); raise(SIGSTOP); printf("Received signal SIGCONT "); return 0; }
Compilation and Execution Steps
Testing SIGSTOP Open Another Terminal and issue following command kill -SIGCONT 30379 or kill -CONT 30379 or kill -18 30379 [1]+ Stopped ./a.out Received signal SIGCONT [1]+ Done ./a.out
In another terminal
kill -SIGCONT 30379
So far, we have seen the program which handles the signal generated by the system. Now, let us see the signal generated through program (using raise() function or through kill command). This program generates signal SIGTSTP (terminal stop), whose default action is to stop the execution. However, since we are handpng the signal now instead of default action, it will come to the defined handler. In this case, we are just printing the message and exiting.
/* signal_raising_handpng.c */ #include<stdio.h> #include<signal.h> #include<stdpb.h> void handler_sigtstp(int signum); int main() { void (*sigHandlerReturn)(int); sigHandlerReturn = signal(SIGTSTP, handler_sigtstp); if (sigHandlerReturn == SIG_ERR) { perror("Signal Error: "); return 1; } printf("Testing SIGTSTP "); raise(SIGTSTP); return 0; } void handler_sigtstp(int signum) { if (signum == SIGTSTP) { printf("Received SIGTSTP "); exit(0); } else printf("Received %d Signal ", signum); return; }
Compilation and Execution Steps
Testing SIGTSTP Received SIGTSTP
We have seen the instances of performing default action or handpng the signal. Now, it is time to ignore the signal. Here, in this sample program, we are registering the signal SIGTSTP to ignore through SIG_IGN and then we are raising the signal SIGTSTP (terminal stop). When the signal SIGTSTP is being generated that would be ignored.
/* signal_raising_ignoring.c */ #include<stdio.h> #include<signal.h> #include<stdpb.h> void handler_sigtstp(int signum); int main() { void (*sigHandlerReturn)(int); sigHandlerReturn = signal(SIGTSTP, SIG_IGN); if (sigHandlerReturn == SIG_ERR) { perror("Signal Error: "); return 1; } printf("Testing SIGTSTP "); raise(SIGTSTP); printf("Signal SIGTSTP is ignored "); return 0; }
Compilation and Execution Steps
Testing SIGTSTP Signal SIGTSTP is ignored
So far, we have observed that we have one signal handler to handle one signal. Can we have a single handler to handle multiple signals? The answer is Yes. Let us consider this with a program.
The following program does the following −
Step 1 − Registers a handler (handleSignals) to catch or handle signals SIGINT (CTRL + C) or SIGQUIT (CTRL + )
Step 2 − If the user generates signal SIGQUIT (either through kill command or keyboard control with CTRL + ), the handler simply prints the message as return.
Step 3 − If the user generates signal SIGINT (either through kill command or keyboard control with CTRL + C) first time, then it modifies the signal to perform default action (with SIG_DFL) from next time.
Step 4 − If the user generates signal SIGINT second time, it performs a default action, which is the termination of program.
/* Filename: sigHandler.c */ #include<stdio.h> #include<unistd.h> #include<signal.h> void handleSignals(int signum); int main(void) { void (*sigHandlerInterrupt)(int); void (*sigHandlerQuit)(int); void (*sigHandlerReturn)(int); sigHandlerInterrupt = sigHandlerQuit = handleSignals; sigHandlerReturn = signal(SIGINT, sigHandlerInterrupt); if (sigHandlerReturn == SIG_ERR) { perror("signal error: "); return 1; } sigHandlerReturn = signal(SIGQUIT, sigHandlerQuit); if (sigHandlerReturn == SIG_ERR) { perror("signal error: "); return 1; } while (1) { printf(" To terminate this program, perform the following: "); printf("1. Open another terminal "); printf("2. Issue command: kill %d or issue CTRL+C 2 times (second time it terminates) ", getpid()); sleep(10); } return 0; } void handleSignals(int signum) { switch(signum) { case SIGINT: printf(" You pressed CTRL+C "); printf("Now reverting SIGINT signal to default action "); signal(SIGINT, SIG_DFL); break; case SIGQUIT: printf(" You pressed CTRL+\ "); break; default: printf(" Received signal number %d ", signum); break; } return; }
Compilation and Execution Steps
To terminate this program, perform the following: 1. Open another terminal 2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates) ^C You pressed CTRL+C Now reverting SIGINT signal to default action To terminate this program, perform the following: 1. Open another terminal 2. Issue command: kill 74 or issue CTRL+C 2 times (second time it terminates) ^You pressed CTRL+ To terminate this program, perform the following: 1. Open another terminal 2. Issue command: kill 120 Terminated
Another Terminal
kill 71
Second Method
To terminate this program, perform the following: 1. Open another terminal 2. Issue command: kill 71 or issue CTRL+C 2 times (second time it terminates) ^C You pressed CTRL+C Now reverting SIGINT signal to default action To terminate this program, perform the following: 1. Open another terminal 2. Issue command: kill 71 or issue CTRL+C 2 times (second time it terminates) ^C
We know that to handle a signal, we have two system calls i.e., either signal() or sigaction(). Till now we have seen with signal() system call, now it is time for sigaction() system call. Let us modify the above program to perform using sigaction() as follows −
/* Filename: sigHandlerSigAction.c */ #include<stdio.h> #include<unistd.h> #include<signal.h> void handleSignals(int signum); int main(void) { void (*sigHandlerReturn)(int); struct sigaction mysigaction; mysigaction.sa_handler = handleSignals; sigemptyset(&mysigaction.sa_mask); mysigaction.sa_flags = 0; sigaction(SIGINT, &mysigaction, NULL); if (mysigaction.sa_handler == SIG_ERR) { perror("signal error: "); return 1; } mysigaction.sa_handler = handleSignals; sigemptyset(&mysigaction.sa_mask); mysigaction.sa_flags = 0; sigaction(SIGQUIT, &mysigaction, NULL); if (mysigaction.sa_handler == SIG_ERR) { perror("signal error: "); return 1; } while (-1) { printf(" To terminate this program, perform either of the following: "); printf("1. Open another terminal and issue command: kill %d ", getpid()); printf("2. Issue CTRL+C 2 times (second time it terminates) "); sleep(10); } return 0; } void handleSignals(int signum) { switch(signum) { case SIGINT: printf(" You have entered CTRL+C "); printf("Now reverting SIGINT signal to perform default action "); signal(SIGINT, SIG_DFL); break; case SIGQUIT: printf(" You have entered CTRL+\ "); break; default: printf(" Received signal number %d ", signum); break; } return; }
Let us see the compilation and execution process. In the execution process, let us see issue CTRL+C twice, remaining checks/ways (as above) you can try for this program as well.
Compilation and Execution Steps
To terminate this program, perform either of the following: 1. Open another terminal and issue command: kill 3199 2. Issue CTRL+C 2 times (second time it terminates) ^C You have entered CTRL+C Now reverting SIGINT signal to perform default action To terminate this program, perform either of the following: 1. Open another terminal and issue command: kill 3199 2. Issue CTRL+C 2 times (second time it terminates) ^CAdvertisements