In this chapter we shall learn about:
1. What is a Signal?
2. What is async and sync signal?
3. How a signal can be generated manually or by the program:
4. Different type of signals:
5. Different ways to handle a signal:
6. Different ways for a process to raise a signal:
7. Different ways to handle a signal:
8. Example 1. “sigaction()” system call.
9. Example 2. “signal()” system call.
What is a Signal?
A Signal is a software interrupt sending to a process.
A signal can be generated synchronously or asynchronously.
A signal is generated synchronously, for example, after a timer got expired.
A signal is generated asynchronously, for example, due to executing program segmentation fault.
A signal also be generated manually or by the program:
A signal can be generated manually, by pressing “Ctrl+c” that sends SIGINT
A signal can be generated by the executing program when there is a segmentation fault, that sends a SIGSEGV signal to the program.
Different type of signals:
Linux has defined 64 signals and start with “SIG”.
Below are some of the signals:
Termination Signals:
These signals are used to tell a process to terminate.
SIGTERM
SIGINT
SIGQUIT
SIGKILL
Alarm Signals:
These signals are used to indicate timer expiry.
SIGALRM
SIGVTALRM
Other signals:
SIGSEGV
SIGBUS
SIGABRT
SIGSYS
Different ways to handle a signal:
A signal can be handled in many different ways.
1. First option is to ignore it. Most of the signals can be ignored, not all. Hardware exceptions like “divide by 0” cannot be ignored.
2. Catch the signal and handle the exception.
3. Apply the default action. Many signals will be having an default action. Like terminate, terminate and core dump, stop the program etc.
Different ways for a process to raise a signal:
1. int raise(getpid(), int S)
Sends a signal “S” to a calling thread.
2. int kill(pid_t PID, int S)
It sends a signal “S” to a process or process group.
3. int pthread_kill(pthread_t TID, int S)
It will send a signal S to a specified thread with thread ID TID.
Different ways to handle a signal:
There are 2 ways that you can handle a signal.
1. By using “sigaction()” system call.
2. By using “signal()” system call.
All the signal system calls are defined in the header file:
#include <signal.h>
1. By using “sigaction()” system call.
int sigaction(int S, const struct sigaction * Act, struct sigaction * OldAct)
You need to fill “sigaction” structure as below:
sa_handler: The signal handler function
sa_flags: Flags to modify the behavior of the handler, or 0
sa_mask: A set of signals to block while this one is being handled
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <signal.h> void sigint_handler(int sig) { printf("received SIGINT\n"); } int main(void) { void sigint_handler(int sig); /* prototype */ char s[200]; struct sigaction sa; /* set the sigaction structure */ sa.sa_handler = sigint_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (sigaction(SIGINT, &sa, NULL) == -1) { perror("sigaction"); exit(1); } while(1) { sleep(1); } return 0; }
Output:
^Creceived SIGINT ^Creceived SIGINT ^Creceived SIGINT
Once you enter “ctrl+c”, you will get above output
2. By using “signal()” system call.
The “signal()” system call is less standard than “sigaction()”.
It is much simpler version also. Take it as a learning purpose, it is recommended to use “sigaction()”.
sighandler_t signal(int signum, sighandler_t handler);
#include<stdio.h> #include<signal.h> #include<unistd.h> void sig_handler(int signo) { if (signo == SIGINT) printf("received SIGINT\n"); } int main(void) { if (signal(SIGINT, sig_handler) == SIG_ERR) printf("\n Cannot catch SIGINT\n"); while(1) { sleep(1); } return 0; }
Output:
^Creceived SIGINT ^Creceived SIGINT ^Creceived SIGINT
Once you enter “ctrl+c”, you will get above output