How Long Does It Take for a Non-Blocked Signal Get Delivered

How long does it take for a non-blocked signal get delivered?

About delivery of signals, TLPI states that signals are "normally" delivered when a task is next scheduled, when switching from kernel mode to user mode, or "immediately" when the task is already running (presumably "immediately" would have to happen by firing an interrupt first, otherwise how could it do that). Well, whatever this means, it is not strictly binding, but it's very close to what happens.

You have to distinguish between realtime and "normal" signals as well as between "normal" signals that are generated synchronously, most of the time because of a hardware event (e.g. segmentation fault) and those that aren't (they're genereated asnychronously).

Realtime signals are queued, normal signals are not. That means that the implementation of normal signals is most likely merely something like one per-task word that serves as a bitmask.

Generating a "normal" signal means setting a bit, and when the OS next decides whether a signal has to be delivered, it tests the word against zero, and if necessary figures out which bit(s) were set, and calls the signal handler(s), if any, accordingly.

The only practical reason why one needs to know this is because it is possible to "lose" signals. If two or more signals are generated before the first is delivered, it's still only one signal alltogether.

The implementation of realtime signals (which are required to queue up to a implementation-dependent length) is obviously much more complicated.

Signals that happen because of a hardware event (e.g. segfault) are generated synchronously, in the same way as if the process called kill on itself (chapter 22.4 TLPI), i.e. they are delivered "immediately", for two reasons. First, it does not make sense to do something else, and second there is already a kernel/user switch happening when the trap handler returns. So delivery is always "immediately" anyway.

Linux - Why is a blocked and ignored signal pending?

Blocking a signal and ignoring it are two separate and independent things.

Ignoring a signal by setting its disposition to SIG_IGN instructs that when the signal is delivered the resulting action should be to do nothing.

Blocking a signal (by setting a signal mask that includes that signal) has the effect of preventing that signal from being delivered at all. If it is received, then it will remain pending until unblocked or the process terminates. Signal disposition does not matter until the signal is actually delivered. So,

It's like the kernel gives priority to the 'blocking' over the 'ignoring'. Is it really the case?

Yes. The effect of ignoring a signal cannot be realized while that signal is blocked.

With regard to the update to the question:

As far as I understand, when the process ignores a signal, it means
that the kernel won't send it to the process.

No, that's incorrect. SIG_IGN is a signal disposition. That's what the process does in response to a signal. It can't respond if the kernel doesn't send the signal in the first place.

Note that another option for signal disposition is for the process to run a custom signal handler function. It should be clearer that this is something that the process must do, not something that the kernel does for it.

This also means that it
won't keep it in the pending list.

It would mean that ignored signals never became pending, but your understanding of the semantics is incorrect.

So the question is, why if we
block+ignore from ahead, the kernel inserts the signal to its pending
list?

Because that's what the kernel does with signals. You can characterize it as what the kernel does with all signals, but those that aren't blocked don't stay pending very long.

Will signals be delivered to a program blocked on POSIX semaphore?

[too long for a comment]

Accessing var from inside the signal handler invokes undefined behaviour (at least for a POSIX conforming system).

From the related POSIX specification:

[...] if the process is single-threaded and a signal handler is executed [...] the behavior is undefined if the signal handler refers to any object [...] with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t [...]

So var shall be defined:

volatile sig_atomic_t var;

The busy waiting while-loop, can be replaced by a single call to a blocking pause(), as it will return on reception of the signal.

From the related POSIX specification:

The pause() function shall suspend the calling thread until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process.

Using pause(), btw, will make the use of any global flag like var redundant, to not say needless.

Does signal auto-block (when execution enters handler function) prevents another such signal from being delivered to another thread?

Reading man sigaction:

sa_mask specifies a mask of signals which should be blocked (i.e., added to the signal mask of the thread in which the signal handler is invoked) during execution of the signal handler. In addition, the signal which triggered the handler will be blocked, unless the SA_NODEFER flag is used.

This sounds like the masking of the currently handled signal only affects the handling thread, so other threads may handle further signals.

I think it is typical for a multithreaded system to block all signals for all threads except one (or several) dedicated signal handling threads (e.g. one that is polling on a signalfd). That way you never have to worry about signals landing on some unpredictable thread.

Delivery of Signal after sigprocmask

To understand it thoroughly we need to understand how signal are generated and delivered in linux.

When kernel receive any signal request it sets signal pending flag for the process, provided signal is not blocked. Now before returning to user mode kernel checks whether there are nonblocked pending signals are present for process or not. If yes, then kernel prefers to deliver that signal before returning to user mode.
Now coming to your question:

if there is a pending signal (say SIGUSR1) that is already unblocked,

I am assuming that signal(SIGUSR1) is first blocked and later unblocked. In this case, when user made an attempt to unblock signal then signal will be delivered before user process resumes in user mode. For ex. if sigprocmask() is used to unblock signal then signal will be delivered even before sigprocmask() returns.

will the signal SIGUSR1 be delivered to the process with a call to
sigprocmask() that unblocks a different signal while keeping SIGUSR1
unblocked?

If multiple pending signals are unblocked and ready to deliver then kernel picks the signal which has lowest signal number( of course, synchronous signals have higher priorities over asynchronous signal) to deliver first.

Understanding signal blocking and signal suspension

If you have a mask that blocks a given signal, do you need to unblock that signal first to allow the program to intercept it or does signal blocking behave in a different way?

A signal is never delivered to any thread that has it blocked. If a signal is raised for a process while all threads of that process have it blocked, then it remains pending until it is unblocked for at least one thread, or the process terminates.

If you use sigsuspend, does your program get suspended until a given signal from the mask you pass as an argument arrives?

No. The signal mask you pass to sigsuspend has the same meaning as the one you pass to (for example) sigprocmask(): it specifies a complete set of the signals that should be blocked. This mask must not include any signals you want the thread to be able to receive. Often, it is appropriate to pass sigsuspend() the mask that was in effect prior to a preceding sigprocmask() call, which the latter function will have provided to you if you asked for it.

Should the signal you wait for when using sigsuspend be unblocked or is it not necessary

At all times, you should ensure that any signal you want a thread to be able to receive is unblocked, and conversely, that any signal the thread must not receive is blocked. This is why sigsuspend() gives you a way to specify a different signal mask to be in effect for the duration of the call.

POSIX signal being blocked in signal handler despite not being in sa_mask

My problem is at a more fundamental level in that if a separate thread sends to both queues sequentially then the sig handler is only run once for the first queue... Which makes me think that somehow SIGIO is being blocked or ignored during the signal handler.

SIGIO is a standard signal, not real-time one.
From POSIX Signal Concepts:

During the time between the generation of a signal and its delivery or acceptance, the signal is said to be "pending". Ordinarily, this interval cannot be detected by an application.

...

If a subsequent occurrence of a pending signal is generated, it is implementation-defined as to whether the signal is delivered or accepted more than once in circumstances other than those in which queuing is required. The order in which multiple, simultaneously pending signals outside the range SIGRTMIN to SIGRTMAX are delivered to or accepted by a process is unspecified.

On Linux, standard signals do not queue, rather they get dropped when one is already pending. From Linux man signal(7):

Queueing and delivery semantics for standard signals

If multiple standard signals are pending for a process, the order in
which the signals are delivered is unspecified.

Standard signals do not queue. If multiple instances of a standard
signal are generated while that signal is blocked, then only one
instance of the signal is marked as pending (and the signal will be
delivered just once when it is unblocked). In the case where a
standard signal is already pending, the siginfo_t structure (see
sigaction(2)) associated with that signal is not overwritten on
arrival of subsequent instances of the same signal. Thus, the
process will receive the information associated with the first
instance of the signal.


One work-around for your problem would be to use SIGEV_THREAD notifications instead of SIGEV_SIGNAL, so that your callback is called by another thread. That also removes the limitation of signal handlers where you can only call async-signal-safe functions.



Related Topics



Leave a reply



Submit