- Table of contents
- What are signals?BACK TO TOC
- Signal as interruptBACK TO TOC
- Signal masksBACK TO TOC
- What signals are good forBACK TO TOC
- Signals that report exceptionsBACK TO TOC
- Other uses of signalsBACK TO TOC
- Types of signalsBACK TO TOC
- Handling exception signalsBACK TO TOC
- SIGKILL and SIGSTOPBACK TO TOC
- SIGKILLBACK TO TOC
- SIGSTOPBACK TO TOC
- Registering signal handlerBACK TO TOC
- signal()BACK TO TOC
- Ignoring signals and restoring original signal handler functionBACK TO TOC
- sigaction()BACK TO TOC
- sigaction() in actionBACK TO TOC
- ConclusionBACK TO TOC
- Linux signals
- Description
- Finding system-specific signals
- Signals in Linux
- Signals not supported by Linux
- Sending signals from the keyboard
- Real-time signals
- Examples: sending signals
- Related commands
Table of contents
Perhaps any engineer developing for Linux encounters this problem. What’s is the right way to terminate the program? What are the ways to receive notifications from operating system about events that occur.
Traditional Unix systems have the answers ready. The answer to these questions is signals.
This article addresses these questions. Here, I’ll try to explain what signals are, their nature. We’ll talk about what are the right ways to handle signals, what signals to handle and what are the pitfalls of signal handling in Linux in particular.
What are signals?BACK TO TOC
Signal is a notification, a message sent by either operating system or some application to your program (or one of its threads).
Each signal identified by a number, from 1 to 31. Signals don’t carry any argument and their names are mostly self explanatory. For instance SIGKILL or signal number 9 tells the program that someone tries to kill it.
Signal as interruptBACK TO TOC
In addition to informative nature of signals, they also interrupt your program. I.e to handle a signal, one of the threads in your program, stops its execution and temporarily switches to signal handler. Note that as in version 2.6 of Linux kernel, most of the signals interrupt only one thread and not the entire application as it used to be once. Moreover, signal handler itself can be interrupted by some other signal.
Signal masksBACK TO TOC
Each one of signals can be in one of three states:
- We may have our own signal handler for the signal.
- Signal may be handled by the default handler. Every signal has its default handler function. For instance, SIGINT default handler will terminate your application.
- Signal may be ignored. Ignoring signal sometimes referred to as blocking signal.
When manipulating signals and managing signal configuration, it is often easier to manage a so called signal mask. It is a bit-mask, where each bit has a corresponding signal. There are 32 (actually 31, 0 doesn’t count) different signals, thus we can use single 32-bit integer (unsigned int) to keep information about 32 signals. This is exactly what operating system does. Moreover, signal masks used as arguments in different system calls, thus we will have to work with signal masks.
The C library assigns default signal handlers. This means that even if you leave signals untouched, your program will process signals and will respond to them according to default behavior. I will describe default signal behavior a little later in this article.
What signals are good forBACK TO TOC
Signals, as their name implies, used to signal something. There are several types of signals, each indicating something of its own. For instance SIGINT that I already mentioned, tells your program that someone tries to interrupt it with CTRL-C.
Dedication of each signal is a matter of semantics. I.e. you may want to decide what action shall be associated with each one of the signals. You may decide that some signal will cause your program to print something or draw something on the screen. It is up to you, most of the time. However, there is a common convention of what each and every signal should do. According to this common convention SIGINT expected to cause your program to terminate itself. This is the default response for SIGINT signal and it is in your interest to keep it this way. It is a question of usability. No one wants a program that cannot be interrupted.
Signals that report exceptionsBACK TO TOC
Another way of using signals is to indicate that that something bad have happened. For instance when your program causes a segmentation fault, operating system sends SIGSEGV signal to your application.
Other uses of signalsBACK TO TOC
Signals have several different usages. For instance debuggers rely on signals to receive events about programs that being debugged (read more about this in my article How Debugger Works). Signals is one of so called IPC – Inter Process Communication mechanisms. IPC used to, as the abbreviation implies, to allow processes communicate with one another.
Another common use is when user wishes that our program will reinitialize itself, but not terminate. In this case, user can send our program a signal from the terminal, using a program called kill. You may be already familiar with this program. It used to kill processes. The truth is that it sends a signal. Each signal has a number that identifies it. By default it sends signal 15, SIGTERM, but it can send just any signal.
Lets see most common and their use.
Types of signalsBACK TO TOC
- SIGHUP
This signal indicates that someone has killed the controlling terminal. For instance, lets say our program runs in xterm or in gnome-terminal. When someone kills the terminal program, without killing applications running inside of terminal window, operating system sends SIGHUP to the program. Default handler for this signal will terminate your program.
Thanks to Mark Pettit for the tip. - SIGINT
This is the signal that being sent to your application when it is running in a foreground in a terminal and someone presses CTRL-C. Default handler of this signal will quietly terminate your program. - SIGQUIT
Again, according to documentation, this signal means “Quit from keyboard”. In reality I couldn’t find who sends this signal. I.e. you can only send it explicitly. - SIGILL
Illegal instruction signal. This is a exception signal, sent to your application by the operating system when it encounters an illegal instruction inside of your program. Something like this may happen when executable file of your program has been corrupted. Another option is when your program loads dynamic library that has been corrupted. Consider this as an exception of a kind, but the one that is very unlikely to happen. - SIGABRT
Abort signal means you used used abort() API inside of your program. It is yet another method to terminate your program. abort() issues SIGABRT signal which in its term terminates your program (unless handled by your custom handler). It is up to you to decide whether you want to use abort() or not. - SIGFPE
Floating point exception. This is another exception signal, issued by operating system when your application caused an exception. - SIGSEGV
This is an exception signal as well. Operating system sends a program this signal when it tries to access memory that does not belong to it. - SIGPIPE
Broken pipe. As documentation states, this signal sent to your program when you try to write into pipe (another IPC) with no readers on the other side. - SIGALRM
Alarm signal. Sent to your program using alarm() system call. The alarm() system call is basically a timer that allows you to receive SIGALRM in preconfigured number of seconds. This can be handy, although there are more accurate timer API out there. - SIGTERM
This signal tells your program to terminate itself. Consider this as a signal to cleanly shut down while SIGKILL is an abnormal termination signal. - SIGCHLD
Tells you that a child process of your program has stopped or terminated. This is handy when you wish to synchronize your process with a process with its child. - SIGUSR1 and SIGUSR2
Finally, SIGUSR1 and SIGUSR2 are two signals that have no predefined meaning and are left for your consideration. You may use these signals to synchronise your program with some other program or to communicate with it.
Handling exception signalsBACK TO TOC
In general I think it would be a good advice to avoid changing signal handler for these signals. Default signal handler for these signals generates core file. Later, you can use core file to analyze the problem and perhaps find a solution. Overwriting signal handler for one of the exception signals, will cause your program to ignore this signal and an exception that has caused the signal. This is something that you don’t want to do.
In case you still want to handle exception signals, read my How to handle SIGSEGV, but also generate a core dump article.
SIGKILL and SIGSTOPBACK TO TOC
These two signals are special. You cannot change how your program handles these two.
SIGKILLBACK TO TOC
SIGKILL, on the contrary to SIGTERM, indicates abnormal termination of the program. You cannot change how your program handles it. It will always terminate your program. However, you can send this signal.
SIGKILL’s value is 9. This is why kill -9
shell command is so effective – it sends SIGKILL signal to the process.
SIGSTOPBACK TO TOC
SIGSTOP used when debugging. When you debug your program, operating system sends SIGSTOP to stop your program, for instance in case it reaches a breakpoint. Operating system does not let you change its handler because you may cause your program to be undebuggable.
Registering signal handlerBACK TO TOC
There are several interfaces that allow you to register your own signal handler.
signal()BACK TO TOC
This is the oldest one. It accepts two arguments, first signal number (one of those SIGsomething) and second pointer to a signal handler function. Signal handler function returns void and accepts single integer argument that represents a signal number that has been sent. This way you can use the same signal handler function for several different signals.
Here is a short code snippet demonstrating how to use it.
This nice and small application registers its own SIGINT signal. Try compiling this small program. See what is happening when you run it and press CTRL-C.
Ignoring signals and restoring original signal handler functionBACK TO TOC
Using signal() you can set default signal handler for certain signal to be used. You can also tell the system that you would like to ignore certain signal. To ignore the signal, specify SIG_IGN as a signal handler. To restore default signal handler, specify SIG_DFL as signal handler.
Although this seems to be everything you may need, it is better to avoid using signal(). There’s a portability problem with this system call. I.e. it behaves differently on different operating systems. There’s a newer system call that does everything signal() does and also gives slightly more information about the actual signal, its origin, etc.
sigaction()BACK TO TOC
sigaction() is another system call that manipulates signal handler. It is much more advanced comparing to good old signal(). Let us take a look at its declaration
Its first argument specifies a signal number. Second and third arguments are pointers to structure called sigaction. This structure specifies how process should handle given signal.
sa_handler is a pointer to the signal handler routine. The routine accepts single integer number containing signal number that it handles and returns void – same as signal handler registered by signal(). In addition, sigaction() let you have more advanced signal handler routine. If needed sa_sigaction pointer should point to the advanced signal handler routine. This one receives much more information about the origin of the signal.
To use sa_sigaction routine, make sure to set SA_SIGINFO flag in sa_flags member of struct sigaction. Similarily to sa_handler, sa_sigaction receives an integer telling it what signal has been triggered. In addition it receives a pointer to structure called siginfo_t. It describes the origin of the signal. For instance, si_pid member of siginfo_t holds the process ID of the process that has sent the signal. There are several other fields that tell you lots of useful information about the signal. You can find all the details on sigaction‘s manual page (man sigaction).
Last argument received by sa_sigaction handler is a pointer to ucontext_t. This type different from architecture to architecture. My advice to you is to ignore this pointer, unless you are writing a new debugger.
One additional advantage of sigaction() compared to signal() is that it allows you to tell operating system what signals can handle signal you are registering. I.e. it gives you full control over what signals can arrive, while your program handling another signal.
To tell this, you should manipulate sa_mask member of the struct sigaction. Note that is a sigset_t field. sigset_t type represents signal masks. To manipulate signal masks, use one of the following functions:
- int sigemptyset(sigset_t *) – to clear the mask.
- int sigfillset(sigset_t *) – to set all bits in the mask.
- int sigaddset(sigset_t *, int signum) – to set bit that represents certain signal.
- int sigdelset(sigset_t *, int signum) – to clear bit that represents certain signal.
- int sigismember(sigset_t *, int signum) – to check status of certain signal in a mask.
sigaction() in actionBACK TO TOC
To conclude, I would like to show a small program that demonstrates sigaction() in use. I would like the program to register signal handler for SIGTERM and then, when it receives the signal, print some information about the origin of the signal.
On the other hand, I will use Python interpretor to send the program a signal.
Here is a program.
And this is what happens when we try to run it. First we run the program.
While it sleeps I ran Python shell and killed it.
Here’s the rest of the output of the program.
We can see that it recognized the process that has killed it and printed its process ID.
ConclusionBACK TO TOC
I hope you found this article interesting. If you have questions, don’t hesitate to email me to alex@alexonlinux.com.
Did you know that you can receive periodical updates with the latest articles that I write right into your email box? Alternatively, you subscribe to the RSS feed!
Want to know how? Check out
Subscribe page
Источник
Linux signals
On Unix-like operating systems such as Linux, signals are software interrupts. They provide a way for the user (or a process) to directly communicate with a process.
Software may be programmed to respond intelligently to a wide array of signals, and certain signals cause processes to behave in a standardized, predefined way at the kernel level.
Description
Process signals were developed as part of UNIX in the 1970s. They are used on all modern Unix-like operating systems, including Linux, BSD, and macOS X.
When a signal is sent to a process, the operating system interrupts the normal flow of the process execution and delivers the notification. If the process has previously registered a way to handle that particular signal, that routine is executed, otherwise the system executes the default signal handler.
Signals can be sent with the kill command, which is named for its default signal (SIGKILL) that instructs the OS to forcefully terminate a process before doing anything else.
Signal names are commonly abbreviated without their SIG prefix, e.g., «KILL», including in the command arguments of kill.
Finding system-specific signals
Signals are defined in the system library signal.h. To view the signals used by your operating system, open a terminal and run man signal or man 7 signal.
Signals in Linux
Signal | Number | Description | Standard |
---|---|---|---|
SIGHUP | 1 | The HUP signal is sent to a process when its controlling terminal is closed. It was originally designed to notify a serial line drop (HUP stands for «Hang Up»). In modern systems, this signal usually indicates the controlling pseudo or virtual terminal is closed. | POSIX |
SIGINT | 2 | The INT signal is sent to a process by its controlling terminal when a user wants to interrupt the process. This signal is often initiated by pressing Ctrl + C , but on some systems, the «delete» character or «break» key can be used. | ANSI |
SIGQUIT | 3 | The QUIT signal is sent to a process by its controlling terminal when the user requests that the process perform a core dump. | POSIX |
SIGILL | 4 | Illegal instruction. The ILL signal is sent to a process when it attempts to execute a malformed, unknown, or privileged instruction. | ANSI |
SIGTRAP | 5 | Trace trap. The TRAP signal is sent to a process when a condition arises that a debugger is tracing — for example, when a particular function is executed, or when a particular variable changes value. | POSIX |
SIGABRT, SIGIOT | 6 | Abort process. ABRT is usually sent by the process itself, when it calls the abort() system call to signal an abnormal termination, but it can be sent from any process like any other signal. SIGIOT is a synonym for SIGABRT. (IOT stands for input/output trap, a signal which originated on the PDP-11.) | 4.2 BSD |
SIGBUS | 7 | The BUS signal is sent to a process when it causes a bus error, such as an incorrect memory access alignment or non-existent physical address. In Linux, this signal maps to SIGUNUSED, because memory access errors of this kind are not possible. | 4.2 BSD |
SIGFPE | 8 | Floating point exception. The FPE signal is sent to a process when it executes erroneous arithmetic operations, such as division by zero. | ANSI |
SIGKILL | 9 | Forcefully terminate a process. With STOP, this is one of two signals which cannot be intercepted, ignored, or handled by the process itself. | POSIX |
SIGUSR1 | 10 | User-defined signal 1. This is one of two signals designated for custom user signal handling. | POSIX |
SIGSEGV | 11 | The SEGV signal is sent to a process when it makes an invalid virtual memory reference, or segmentation fault, i.e., when it performs a segmentation violation. | |
SIGUSR2 | 12 | User-defined signal 2. This is one of two signals designated for custom user signal handling. | POSIX |
SIGPIPE | 13 | The PIPE signal is sent to a process when it attempts to write to a pipe without a process connected to the other end. | POSIX |
SIGALRM | 14 | The ALRM signal notifies a process that the time interval specified in a call to the alarm() system function has expired. | POSIX |
SIGTERM | 15 | The TERM signal is sent to a process to request its termination. Unlike the KILL signal, it can be caught and interpreted or ignored by the process. This signal allows the process to perform nice termination releasing resources and saving state if appropriate. It should be noted that SIGINT is nearly identical to SIGTERM. | ANSI |
SIGSTKFLT | 16 | Stack fault. Maps to SIGUNUSED in Linux. | |
SIGCHLD | 17 | The CHLD signal is sent to a process when a child process terminates, is interrupted, or resumes after being interrupted. One common usage of the signal is to instruct the operating system to clean up the resources used by a child process after its termination without an explicit call to the wait system call. | POSIX |
SIGCONT | 18 | Continue executing after stopped, e.g., by STOP | POSIX |
SIGSTOP | 19 | The STOP signal instructs the operating system to stop a process for later resumption. This is one of two signals, along with KILL that cannot be intercepted, ignored, or handled by the process itself. | POSIX |
SIGTSTP | 20 | The TSTP signal is sent to a process by its controlling terminal to request it to stop temporarily. It is commonly initiated by the user pressing Ctrl + Z . Unlike SIGSTOP, this process can register a signal handler for or ignore the signal. | POSIX |
SIGTTIN | 21 | The TTIN signal is sent to a process when it attempts to read from the tty while in the background. This signal can be received only by processes under job control. Daemons do not have controlling terminals and should never receive this signal. | POSIX |
SIGTTOU | 22 | TTOU signal is sent to a process when it attempts to write from the tty while in the background. The compliment to TTIN. | POSIX |
SIGURG | 23 | The URG signal is sent to a process when a socket has urgent or out-of-band data available to read. | 4.2 BSD |
SIGXCPU | 24 | The XCPU signal is sent to a process when it has used up the CPU for a duration that exceeds a certain predetermined user-settable value. The arrival of an XCPU signal provides the receiving process a chance to quickly save any intermediate results and to exit gracefully, before it is terminated by the operating system using the SIGKILL signal. | 4.2 BSD |
SIGXFSZ | 25 | The XFSZ signal is sent to a process when it grows a file larger than the maximum allowed size. | 4.2 BSD |
SIGVTALRM | 26 | Virtual alarm clock. May be sent by the alarm() system call. By default, this signal kills the process, but it’s intended for use with process-specific signal handling. | 4.2 BSD |
SIGPROF | 27 | Profiling alarm clock. Indicates expiration of a timer that measures CPU time used by the current process («user» time), and CPU time expended on behalf of the process by the system («system» time). These times may be used to implement code profiling facilities. By default, this signal terminates the process, but it’s intended for use with process-specific signal handling. | 4.2 BSD |
SIGWINCH | 28 | Window change. The WINCH signal is sent to a process when its controlling terminal changes size, for instance if you resize it in your window manager. | 4.3 BSD, Sun |
SIGIO, SIGPOLL | 29 | Input/output is now possible. SIGPOLL is a synonym for SIGIO, and in Linux its behavior is identical to SIGURG. | 4.2 BSD |
SIGPWR, SIGLOST | 30 | Power failure. The PWR signal is sent to a process when the system detects a power failure. SIGLOST is a synonym for SIGPWR. | System V |
SIGUNUSED, SIGSYS | 31 | Unused signal. This signal is provided for compatibility reasons, for example when porting software from an operating system with different or unsupported signals in Linux. In Linux, SIGSYS is a synonym for SIGUNUSED. | System V r4 |
Signals not supported by Linux
The following signals may be used by other systems, such as BSD, but are interpreted as SIGUNUSED in Linux.
SIGEMT | The EMT signal is sent to a process when an emulator trap occurs. Unused in Linux. |
SIGINFO | The INFO signal is sent to a process when a status request is received from the controlling terminal. Unused in Linux |
SIGLOST | The LOST signal is sent to a process when a file lock is lost. Unused in Linux. |
SIGSYS | The SYS signal is sent to a process when it passes a bad argument to a system call. Unused in Linux. |
Sending signals from the keyboard
Signals may be sent from the keyboard. Several standard defaults are listed below. Default key combinations for sending interrupt signals can be defined with the stty command.
Ctrl-C | Send SIGINT (Interrupt). By default, this causes a process to terminate. |
Ctrl-Z | Send SIGTSTP (Suspend). By default, this causes a process to suspend all operation. |
Ctrl-\ | Send SIGQUIT (Quit). By default, this causes a process to terminate immediately and dump the core. |
Ctrl-T | Send SIGINFO (Info). By default, this causes the operating system to display information about the command. Not supported on all systems. |
Real-time signals
Real-time signals are a set of signals with no predefined purpose, for programmers to use as they want in their software. Two signal names, SIGRTMIN and SIGRTMAX, define the minimum and maximum signal numbers of the real-time signals. For example, the programmer may use the signal number as SIGRTMIN+3 to refer to the fourth real-time signal number.
Examples: sending signals
The kill command sends signals to processes. Your shell may have a built-in version of kill, which supersedes the version installed at /bin/kill. The two versions have slightly different options, but basic functions are the same. The following examples may run using either version of kill.
The process to be signaled is referred to by PID (process ID). If you’re not sure of the process ID, you can find it with the ps command, for example ps -aux.
Send the KILL signal to the process with PID 1234.
Kill three processes: PIDs 123, 456, and 789.
Send signal number 15 (TERM) to processes 1234 and 5678.
Same as the previous command.
List all available signals. Example output:
The special process ID -1 refers to all processes other than kill and the system root process. This command attempts to kill (-9) every possible process (-1) on the system. For more information, see the documentation of kill, linked below.
Related commands
kill — End a process.
stty — Change terminal line settings.
Источник