Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linux does not implement POSIX signal queuing?

The POSIX article for sigaction states:

If SA_SIGINFO is set in sa_flags, then subsequent occurrences of sig generated by sigqueue() or as a result of any signal-generating function that supports the specification of an application-defined value (when sig is already pending) shall be queued in FIFO order until delivered or accepted; the signal-catching function shall be invoked with three arguments. The application specified value is passed to the signal-catching function as the si_value member of the siginfo_t structure.

This says nothing about merging of occurrences of the same signal (signo). And even if it meant merging, then the phrase about FIFO would be incomplete. For example, if the FIFO is [SIGALRM, SIGIO, SIGUSR1, SIGIO], what would it be after merging, [SIGALRM, SIGIO, SIGUSR1] or [SIGALRM, SIGUSR1, SIGIO]?

However, I saw several reports (0, 1, 2) that Linux indeed merges occurrences of the same signal. I also wrote a small test program that confirms that occurrences of the same signal sent with sigqueue when that signal is blocked are merged on ArchLinux. A signal handler was installed with sigaction.

Linux [skipped] 5.3.8-arch1-1 #1 SMP PREEMPT [skipped] x86_64 GNU/Linux

If occurrences of the same signal are merged, then transmitting information via union sigval does not make sense because this information may be unpredictably lost.

Is my understanding of POSIX incorrect, or is Linux implementing it incorrectly?

[Update 2019-11-09 09:51:32+00:00. I found another chunk of documentation that corroborates my point. According to the article “2. General Information”,

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.

and another one on sigqueue,

If SA_SIGINFO is set for signo and if the resources were available to queue the signal, the signal shall be queued and sent to the receiving process.

The sigqueue() function shall fail if:

[EAGAIN] No resources are available to queue the signal. The process has already queued {SIGQUEUE_MAX} signals that are still pending at the receiver(s), or a system-wide resource limit has been exceeded.

I used sigaction with SA_SIGINFO set and sigqueue. The function sigqueue did not return EAGAIN.]

[Update 2019-11-09 17:45:25+00:00. My test program. The sigprint functions are for printing using write and a statically allocated buffer; they are not important.

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include "sigprint.h"

void signal_handler(int signo, siginfo_t *info, void *context) {
    sigprint_string("|");
    sigprint_long(info->si_value.sival_int);
    sigprint_flush();
}

void sigqueue_check(pid_t pid, int signo, union sigval value) {
    if (sigqueue(pid, signo, value) == -1) {
        printf(" returned error code %d which is %s to EAGAIN.\n",
               errno, errno == EAGAIN ? "==" : "!=");
        exit(24);
    }
}

int main() {
    struct sigaction sigaction0;
    union sigval sigval0;
    sigset_t sigset0, sigset1;
    sigaction0.sa_sigaction = &signal_handler;
    sigemptyset(&sigaction0.sa_mask);
    sigaction0.sa_flags = SA_SIGINFO | SA_RESTART;
    sigaction(SIGALRM, &sigaction0, NULL);

    sigemptyset(&sigset0);
    sigaddset(&sigset0, SIGALRM);
    sigprocmask(SIG_BLOCK, &sigset0, &sigset1);
    sigval0.sival_int = 10;
    sigqueue_check(getpid(), SIGALRM, sigval0);
    sigval0.sival_int = 11;
    sigqueue_check(getpid(), SIGALRM, sigval0);
    sigval0.sival_int = 12;
    sigqueue_check(getpid(), SIGALRM, sigval0);
    sigprocmask(SIG_SETMASK, &sigset1, NULL);
    sleep(1);
    return 0;
}

]

like image 483
beroal Avatar asked Sep 18 '25 06:09

beroal


1 Answers

The documented behavior on Linux is to not queue duplicate blocked signals.

From man 7 signal:

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.

However... the man page also says:

Multiple instances of real-time signals can be queued. By contrast, if multiple instances of a standard signal are delivered while that signal is currently blocked, then only one instance is queued.

Are you looking at the POSIX documentation for real time signals and testing with standard ones?

like image 186
Shawn Avatar answered Sep 20 '25 21:09

Shawn