Default signal handlers and EINTR - linux

Is it safe to assume that a system call will never return an EINTR error when all signal handlers are set to default action?
If not, is there a way to force this behavior (for example, handling signals in a separate thread)?

Related

Interruption of signal handler with other signal?

Can a signal handler be interrupted by another signal (except of SIGKILL, SIGSTOP)?
Therefore, do I need to check for EINTR in my signal handler when calling interruptable syscalls?
(Linux and other Unixes)
Yes, the execution of a signal handler may itself be interrupted by the delivery of another signal.
There are a few nuances, however.
By default, user-defined signal handlers temporarily block the very signal which invoked them. This is the default behavior of sigaction unless the SA_NODEFER flag is set. (This is also the behavior of the older, discouraged signal function, at least on most implementations.)
Additionally, sigaction can explicitly block signals during the handler's execution by setting the sa_mask member of the const struct sigaction. Most code you see will explicitly empty this member during struct initialization, though it is often more robust to sigfillset this member and not worry about interruptions.
So, again, yes. Handle EINTR, errno, &c. as appropriate, or, better yet, design the handler to do no more than set a sig_atomic_t flag and avoid many worries.

Proper way to chain signal handlers in linux?

I've got a couple signal handlers I'm using to exit my program cleanly, and I'd like to play nicely with whatever else has hooked them by chaining the signal handlers calls. I'm using sigaction per the man page for signal saying it's not preferred anymore.
Looking at the sigaction struct, there's signal masks and such that are specified, along with several flags. What's the "right" way to call the currently installed handler so that all those options are taken into account?
The answer is "it depends": on what the signal handlers do:
The first reaction from many will be that a signal handler will be used to handle a SIGINT, etc., and terminate a program cleanly.
On the other hand, there are (more or less) benign signals such as SIGWINCH (which you would like to not stop your program).
In "terminate a program cleanly", there may not be room for other programs to do something useful. For instance, the proposed chained-handler may close files that you rely upon. So you have to start with a good knowledge of what the other handlers do.
If the signal handler is in the latter class, simply updating a variable which the application can test, then the signal handler function is just another function. When you call signal to associate a signal with a function, that returns the previous handler (which may be one of the magic values, e.g.,. SIG_DFL, SIG_IGN). If you save that, it's possible to check if it is none of those, and (presumably) a genuine function. Calling that function would continue execution as if it were part of your current signal handler.
It is not a new idea (I used it in the late 1990s), and is discussed occasionally:
signal() overwriting other signal handlers
sigaction - how to find and invoke previous signal handler (sa_handler)
Use reentrant functions for safer signal handling
And of course:
signal - signal management
signal.h - signals
Well, the proper answer IMO is "Don't do that". I think you should reconsider if you plan to chain signal handlers.
Basically, if you have something so critical it has to be cleaned up even if a fatal signal arrives, then you should do that part quickly and reset the signal handlers before letting any other code run.
Signals such as SIGTERM and SIGQUIT should be handled by events that terminate your program in the normal fashion. Typically your signal handler writes on a pipe to message the normal event loop in the application, or sets a global variable if you don't have an event loop.
Perhaps you know this, but please also make sure to read the list of functions that are safe to call from a signal handler. It should be in the man page. Anything except for that list is not safe.

select system occaionally not failing with EINTR

Using timer_create, we deliver a real time signal to a thread which waits on select function.
This signal is caught and handled in the thread. Based on fact the select will be interrupted when a signal is caught, I have some logic implemented if select fails with error number EINTR.
This works fine most of the time, but occasionally I notice that select is not getting interrupted (or some how the code within EINTR case not getting executed).
What are possible reasons for this?
It can be that when the timer expiry signal is delivered you are not waiting in select, hence it does not return EINTR.
If you want to receive EINTR only when the thread is blocked in select, you may block that signal in the thread using pthread_sigmask and use pselect or epoll_pwait that would unblock that signal while waiting only. This way the rest of your code does not need to be concerned with handling EINTR.
If you have more than one thread in the process make sure you block that signal in all other threads, so that only one thread gets delivered that signal. See Signal Concepts for more details.
A more elegant option (IMO) is to avoid using timer_create and rather pass the delay to the next timer expiry as select time-out argument (this is what libevent does). But that requires you to maintain your own min-heap of timers.

how and when -EINTR is set when linux system call is blocked

If a system call is blocked , the process state is set to TASK_INTERRUPTIBLE, and the process is removed from run queue.
When a signal is delivered to that process, kernel adds the signal to list of pending signals and sets the process state to TASK_RUNNING.
And when next time schedule() is called this process is executed.
What i did not understand is how exactly blocked system call returns -EINTR to userspace?
Any blocked system call can return -EINTR?
The logic of setting -EINTR is done by signal handling code or by system call itself?
AFAIK signal handling only happens before returning to userspace, is that true?
Does signal handling happens during context switch?
Please help me understand this.
When the process is running again (i.e., when schedule() returns), the driver must check for this case with the signal_pending() function, and abort what it's doing and return the -EINTR error code.
Many system calls are restartable, i.e., after interrupted by a signal, they could be just executed again without changing the functionality.
In that case, they return -ERESTARTSYS instead of -EINTR, and the kernel will handle the restarting automatically after the signal has been handled.
For an example, see the function uart_wait_modem_status in drivers/tty/serial/serial_core.c, or any other place where EINTR or ERESTARTSYS are used.

What happens when a signal is received while already in a signal handler?

I have a parent process spawning several child processes. I want to know when any child process exits by registering a SIGCHLD signal handler.
The question is, what happens if another SIGCHLD (or any other signal) is received, while the parent process is already in a signal handler?
I can think of the following outcomes:
The signal is ignored
The signal is queued, and will be processed as soon as the current handler returns
The current handler is in turn interrupted, just like the main program
Which one is correct?
In your concrete example (the same signal being received), the signal is delivered after the signal handler has finished (so bullet point #2 is correct). Note, however, that you may "lose" signals.
The reason for that is that while a signal is being inside its handler, it is blocked. Blocked signals are set to pending, but not queued. The term "pending" means that the operating system remembers that there is a signal waiting to be delivered at the next opportunity, and "not queued" means that it does this by setting a flag somewhere, but not by keeping an exact record of how many signals have arrived.
Thus, you may receive 2 or 3 (or 10) more SIGCHLD while in your handler, but only see one (so in some cases, bullet point #1 can be correct, too).
Note that several flags that you can pass to sigaction can affect the default behaviour, such as SA_NODEFER (prevents blocking signal) and SA_NOCLDWAIT (may not generate signal at all on some systems).
Now of course, if you receive a different type of signal, there's no guarantee that it won't interrupt your handler. For that reason, one preferrably doesn't use non signal safe functions.

Resources