What are signals in linux

Unix / Linux — Signals and Traps

In this chapter, we will discuss in detail about Signals and Traps in Unix.

Signals are software interrupts sent to a program to indicate that an important event has occurred. The events can vary from user requests to illegal memory access errors. Some signals, such as the interrupt signal, indicate that a user has asked the program to do something that is not in the usual flow of control.

The following table lists out common signals you might encounter and want to use in your programs −

Signal Name Signal Number Description
SIGHUP 1 Hang up detected on controlling terminal or death of controlling process
SIGINT 2 Issued if the user sends an interrupt signal (Ctrl + C)
SIGQUIT 3 Issued if the user sends a quit signal (Ctrl + D)
SIGFPE 8 Issued if an illegal mathematical operation is attempted
SIGKILL 9 If a process gets this signal it must quit immediately and will not perform any clean-up operations
SIGALRM 14 Alarm clock signal (used for timers)
SIGTERM 15 Software termination signal (sent by kill by default)

List of Signals

There is an easy way to list down all the signals supported by your system. Just issue the kill -l command and it would display all the supported signals −

The actual list of signals varies between Solaris, HP-UX, and Linux.

Default Actions

Every signal has a default action associated with it. The default action for a signal is the action that a script or program performs when it receives a signal.

Some of the possible default actions are −

Terminate the process.

Ignore the signal.

Dump core. This creates a file called core containing the memory image of the process when it received the signal.

Stop the process.

Continue a stopped process.

Sending Signals

There are several methods of delivering signals to a program or script. One of the most common is for a user to type CONTROL-C or the INTERRUPT key while a script is executing.

When you press the Ctrl+C key, a SIGINT is sent to the script and as per defined default action script terminates.

The other common method for delivering signals is to use the kill command, the syntax of which is as follows −

Here signal is either the number or name of the signal to deliver and pid is the process ID that the signal should be sent to. For Example −

The above command sends the HUP or hang-up signal to the program that is running with process ID 1001. To send a kill signal to the same process, use the following command −

This kills the process running with process ID 1001.

Trapping Signals

When you press the Ctrl+C or Break key at your terminal during execution of a shell program, normally that program is immediately terminated, and your command prompt returns. This may not always be desirable. For instance, you may end up leaving a bunch of temporary files that won’t get cleaned up.

Trapping these signals is quite easy, and the trap command has the following syntax −

Here command can be any valid Unix command, or even a user-defined function, and signal can be a list of any number of signals you want to trap.

There are two common uses for trap in shell scripts −

  • Clean up temporary files
  • Ignore signals

Cleaning Up Temporary Files

As an example of the trap command, the following shows how you can remove some files and then exit if someone tries to abort the program from the terminal −

From the point in the shell program that this trap is executed, the two files work1$$ and dataout$$ will be automatically removed if signal number 2 is received by the program.

Hence, if the user interrupts the execution of the program after this trap is executed, you can be assured that these two files will be cleaned up. The exit command that follows the rm is necessary because without it, the execution would continue in the program at the point that it left off when the signal was received.

Signal number 1 is generated for hangup. Either someone intentionally hangs up the line or the line gets accidentally disconnected.

You can modify the preceding trap to also remove the two specified files in this case by adding signal number 1 to the list of signals −

Now these files will be removed if the line gets hung up or if the Ctrl+C key gets pressed.

The commands specified to trap must be enclosed in quotes, if they contain more than one command. Also note that the shell scans the command line at the time that the trap command gets executed and also when one of the listed signals is received.

Thus, in the preceding example, the value of WORKDIR and $$ will be substituted at the time that the trap command is executed. If you wanted this substitution to occur at the time that either signal 1 or 2 was received, you can put the commands inside single quotes −

Ignoring Signals

If the command listed for trap is null, the specified signal will be ignored when received. For example, the command −

This specifies that the interrupt signal is to be ignored. You might want to ignore certain signals when performing an operation that you don’t want to be interrupted. You can specify multiple signals to be ignored as follows −

Note that the first argument must be specified for a signal to be ignored and is not equivalent to writing the following, which has a separate meaning of its own −

If you ignore a signal, all subshells also ignore that signal. However, if you specify an action to be taken on the receipt of a signal, all subshells will still take the default action on receipt of that signal.

Читайте также:  Не запускается windows с usb флешки

Resetting Traps

After you’ve changed the default action to be taken on receipt of a signal, you can change it back again with the trap if you simply omit the first argument; so −

This resets the action to be taken on the receipt of signals 1 or 2 back to the default.

Источник

All about Linux signals

Table of Contents:

  • All about Linux signals
  • Introduction
  • What is signaled, signal handlers
  • Handling specific signals: SIGCHLD, SIGBUS, SIGSEGV, SIGABRT
  • What happens when a process receives a signal, system call interruption
  • Blocking signals
  • Waiting for a signal
  • Sending signals
  • Real-time signals
  • Signals and fork()
  • Signals and threads
  • Other uses of signals
  • That’s not everything!

What is signaled in Linux

For a complete list of signals see the signal(7) manual page.

Signal handlers

Traditional signal() is deprecated

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:

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 the siginfo_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 the SA_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 the sa_sigaction handler you must use SA_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()

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

We’ll see more examples of use of siginfo_t later.

Compiler optimization and data in signal handler

What it does? It depends on compiler optimization settings. Without optimization it executes a loop that ends when the process receives SIGTERM or other sgnal that terminates the process and was not handler. When you compile it with the -O3 gcc flag it will not exit after receiving SIGTERM . Why? because whe while loop is optimized in such way that the exit_flag variable is loaded into a processor register once and not read from the memory in the loop. The compiler isn’t aware that the loop is not the only place where the program accesses this variable while running the loop. In such cases — modifying a variable in a signal handler that is also accessed in some other parts of the program you must remember to instruct the compiler to always access this variable in memory when reading or writing them. You should use the volatile keyword in the variable declaration:

Atomic Type

  • It doesn’t work like a mutex: it’s guaranteed that read or write of this type translates into an uninterruptible operation but code such as:

Isn’t safe: there is read and update in the if operation but only single reads and single writes are atomic.

  • Don’t try to use this type in a multi-threaded program as a type that can be used without a mutex. It’s only intended for signal handlers and has nothing to do with mutexes!
  • You don’t need to worry if data are modified or read in a signal handler are also modified or read in the program if it happens only in parts where the signal is blocked. Later I’ll show how to block signals. But you will still need the volatile keyword.

Signal-safe functions

Alternative method of handling signals: signalfd()

First we must block the signals we want to handle with signalfd(2) using sigprocmask(2). This function will be described later. Then we call signalfd(2) to create a file descriptor that will be used to read incoming signals. At this point in case of SIGTERM or SIGINT delivered to your program it will not be interrupted, no handler will be called. It will be queued and you can read information about it from the sfd descriptor. You must supply a buffer large enough to read the struct signalfd_siginfo object that will be filled with information similar to the previously described siginfo_t . The difference is that the fields are named a bit different (like ssi_signo instead of si_signo ). What is interesting is that the sfd descriptor behaves and can be used just like any other file descriptor, in particular you can:

  • Use it in select(2), poll(2) and similar functions.
  • Make it non-blocking.
  • Create many of them, each handling different signals to return different descriptors as ready by select(2) for every different signal.
  • After fork() the file descriptor is not closed, so the child process can read signals that were send to the parent.

This is perfect to be used in a single-process server with the main loop executes a function like poll(2) to handle many connections. It simplifies signal handling because the signal descriptor can be added to the poll’s array of descriptors and handled like any other of them, without asynchronous actions. You handle the signal when you are ready for that because your program is not interrupted.

Источник

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.
Читайте также:  Droidcam для mac os

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

Источник

Оцените статью