As discussed extensively in the comments, it is important to use POSIX function sigaction()
rather than the standard C function signal()
because there are many implementation-defined aspects to signal()
(primarily because there were many divergent implementations before the C standard was created, and the standard tried to accommodate existing implementations without breaking any of them).
However, the system is not obligated to queue signals that are not real-time signals (signal numbers in the range SIGRTMIN..SIGRTMAX). SIGUSR1 is not a real-time signal. Frankly, even with signal queueing, I'm not sure whether implementations would handle up to 255 pending signals of a specific type for a process — it isn't an area I've experimented with.
This is the best code I was able to come up with:
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifndef SEND_SIGNAL
#define SEND_SIGNAL SIGUSR1
#endif
static const char *arg0;
static volatile sig_atomic_t nreceived = 0;
static _Noreturn void err_syserr(const char *syscall);
static void handler(int sig)
{
assert(sig == SEND_SIGNAL);
nreceived++;
}
int main(int argc, char **argv)
{
if (argc != 1)
{
fprintf(stderr, "Usage: %s\n", argv[0]);
exit(EXIT_FAILURE);
}
arg0 = argv[0];
struct sigaction sa = { .sa_handler = handler, .sa_flags = SA_RESTART };
/* Block all blockable signals */
if (sigfillset(&sa.sa_mask) != 0)
err_syserr("sigfillset");
if (sigaction(SEND_SIGNAL, &sa, 0) != 0)
err_syserr("sigaction");
pid_t pid = fork();
if (pid > 0)
{
int status;
int corpse = wait(&status);
if (corpse != -1)
printf("Child process %d exited with status 0x%.4X\n", corpse, status);
else
fprintf(stderr, "%s: wait() failed: (%d) %s\n", argv[0], errno, strerror(errno));
printf("Caught %d signals from process %d\n", nreceived, pid);
}
else if (pid == 0)
{
srand(time(NULL));
int nsignals = rand() % 256;
for (int i = 0; i < nsignals; i++)
kill(getppid(), SEND_SIGNAL);
printf("Sent %d signals to process %d\n", nsignals, getppid());
}
else
err_syserr("fork");
return 0;
}
static _Noreturn void err_syserr(const char *syscall)
{
fprintf(stderr, "%s: %s() failed: (%d) %s\n", arg0, syscall, errno, strerror(errno));
exit(EXIT_FAILURE);
}
When run as program sig53
(source code sig53.c
) on a Mac running macOS Monterey 12.3.1, I got variable numbers of signals received:
$ sig53
Sent 50 signals to process 37973
Child process 37974 exited with status 0x0000
Caught 14 signals from process 37974
$: sig53
Sent 39 signals to process 38442
Child process 38443 exited with status 0x0000
Caught 16 signals from process 38443
$: sig53
Sent 28 signals to process 38478
Child process 38479 exited with status 0x0000
Caught 6 signals from process 38479
$
Sometimes, the number received reached near 100, but never very near to all the signals sent.
YMMV on Linux. There may be alternative mechanisms for handling signals on Linux. But for portable code, sending a myriad signals to a single process at full tilt is not a reliable way of communicating between processes. Some of the signals will be delivered, but it may not be all of them.