10 KiB
created_at | title | url | author | points | story_text | comment_text | num_comments | story_id | story_title | story_url | parent_id | created_at_i | _tags | objectID | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2016-01-17T12:29:15.000Z | All about Linux signals (2009) | http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show | ingve | 124 | 8 | 1453033755 |
|
10919277 |
All about Linux signals | Linux Programming Blog
** Linux Programming Blog **
const char s[] = "Linux Programming Blog";
Main menu
Home › Blogs › daper's blog ›
All about Linux signals
Submitted by daper on Tue, 02/03/2009 - 00:33
In most cases if you want to handle a signal in your application you write a simple signal handler like:
void handler (int sig)
and use the signal(2) system function to run it when a signal is delivered to the process. This is the simplest case, but signals are more interesting than that! Information contained in this article is useful for example when you are writing a daemon and must handle interrupting your program properly without interrupting the current operation or the whole program.
What is covered here
The article describes how signals work in Linux and how to handle signals using POSIX API. I will cover functions working on every modern Linux system, but it should also apply to most POSIX systems unless explicitly stated otherwise. Legacy functions are not covered. A basic knowledge about signals is required.
What is signaled in Linux
Your process may receive a signal when:
- From user space from some other process when someone calls a function like kill(2).
- When you send the signal from the process itself using a function like abort(3).
- When a child process exits the operating system sends the
SIGCHLD
signal. - When the parent process dies or hangup is detected on the controlling terminal
SIGHUP
is sent. - When user interrupts program from the keyboard
SIGINT
is sent. - When the program behaves incorrectly one of
SIGILL, SIGFPE, SIGSEGV
is delivered. - When a program accesses memory that is mapped using mmap(2) but is not available (for example when the file was truncated by another process) - really nasty situation when using mmap() to access files. There is no good way to handle this case.
- When a profiler like gprof is used the program occasionally receives
SIGPROF
. This is sometimes problematic when you forgot to handle interrupting system functions like read(2) properly (errno == EINTR
). - When you use the write(2) or similar data sending functions and there is nobody to receive your data
SIGPIPE
is delivered. This is a very common case and you must remember that those functions may not only exit with error and setting the errno variable but also cause theSIGPIPE
to be delivered to the program. An example is the case when you write to the standard output and the user uses the pipeline sequence to redirect your output to another program. If the program exits while you are trying to send dataSIGPIPE
is sent to your process. A signal is used in addition to the normal function return with error because this event is asynchronous and you can't actually tell how much data has been successfully sent. This can also happen when you are sending data to a socket. This is because data are buffered and/or send over a wire so are not delivered to the target immediately and the OS can realize that can't be delivered after the sending function exits.
For a complete list of signals see the signal(7) manual page.
Signal handlers
Traditional signal() is deprecated
The signal(2) function is the oldest and simplest way to install a signal handler but it's deprecated. There are few reasons and most important is that the original Unix implementation would reset the signal handler to it's default value after signal is received. If you need to handle every signal delivered to your program separately like handling SIGCHLD
to catch a dying process there is a race here. To do so you would need to set to signal handler again in the signal handler itself and another signal may arrive before you cal the signal(2) function. This behavior varies across different systems. Moreover, it lacks features present in sigaction(2) you will sometimes need.
The recommended way of setting signal actions: sigaction
The sigaction(2) function is a better way to set the signal action. It has the prototype:
1. int sigaction (int signum, const struct sigaction *act, struct sigaction *oldact);
As you can see you don't pass the pointer to the signal handler directly, but instead a struct sigaction
object. It's defined as:
1. struct sigaction {
2. void (*sa_handler)(int);
3. void (*sa_sigaction)(int, siginfo_t *, void *);
4. sigset_t sa_mask;
5. int sa_flags;
6. void (*sa_restorer)(void);
7. };
For a detailed description of this structure's fields see the sigaction(2) manual page. Most important fields are:
sa_handler
- This is the pointer to your handler function that has the same prototype as a handler for signal(2).sa_sigaction
- This is an alternative way to run the signal handler. It has two additional arguments beside the signal number where thesiginfo_t *
is the more interesting. It provides more information about the received signal, I will describe it later.sa_mask
allows you to explicitly set signals that are blocked during the execution of the handler. In addition if you don't use theSA_NODEFER
flag the signal which triggered will be also blocked.sa_flags
allow to modify the behavior of the signal handling process. For the detailed description of this field, see the manual page. To use thesa_sigaction
handler you must useSA_SIGINFO
flag here.
What is the difference between signal(2) and sigaction(2) if you don't use any additional feature the later one provides? The answer is: portability and no race conditions. The issue with resetting the signal handler after it's called doesn't affect sigaction(2), because the default behavior is not to reset the handler and blocking the signal during it's execution. So there is no race and this behavior is documented in the POSIX specification. Another difference is that with signal(2) some system calls are automatically restarted and with sigaction(2) they're not by default.
Example use of sigaction()
See example of using sigaction() to set a signal handler with additional parameters.
In this example we use the three arguments version of signal handler for SIGTERM
. Without setting the SA_SIGINFO
flag we would use a traditional one argument version of the handler and pass the pointer to it by the sa_handler
field. It would be a replacement for signal(2). You can try to run it and do kill PID to see what happens.
In the signal handler we read two fields from the siginfo_t *siginfo
parameter to read the sender's PID and UID. This structure has more fields, I'll describe them later.
The sleep(3) function is used in a loop because it's interrupted when the signal arrives and must be called again.
SA_SIGINFO handler
In the previous example SA_SIGINFO
is used to pass more information to the signal handler as arguments. We've seen that the siginfo_t
structure contains si_pid
and si_uid
fields (PID and UID of the process that sends the signal), but there are many more. They are all described in sigaction(2) manual page. On Linux only si_signo
(signal number) and si_code
(signal code) are available for all signals. Presence of other fields depends on the signal type. Some other fields are:
si_code
- Reason why the signal was sent. It may beSI_USER
if it was delivered due to kill(2) or raise(3),SI_KERNEL
if kernel sent it and few more. For some signals there are special values likeILL_ILLADR
telling you thatSIGILL
was sent due to illegal addressing mode.- For
SIGCHLD
fieldssi_status, si_utime, si_stime
are filled and contain information about the exit status or the signal of the dying process, user and system time consumed. - In case of
SIGILL, SIGFPE, SIGSEGV, SIGBUS
si_addr
contains the memory address that caused the fault.
We'll see more examples of use of siginfo_t
later.
Compiler optimization and data in signal handler
Let's see the following example:
1. #include <stdio.h>
2. #include <unistd.h>
3. #include <signal.h>
4. #include <string.h>
5.
6. static int exit_flag = 0;
7.
8. static void hdl (int sig)
9. {
10. exit_flag = 1;
11. }
12.
13. int main (int argc, char *argv[])
14. {
15. struct sigaction act;
16.
17. memset (&act, '