Skip to content

kill · sigaction · getpid

Sent is not delivered

kill makes a signal pending and returns. Nothing happens to the target until it next crosses a syscall boundary — which is most of why a shutdown that “does not work” turns out to be one that was never delivered.

Two separate things, and a gap between them

Sending a signal sets a bit. kill returns immediately, and it returns successfully whether or not the target ever notices — it has said nothing about delivery, only about the signal now being pending.

Delivery happens later, when the target next crosses from its own code into the kernel and back. That is a syscall boundary, and it is the only place a kernel checks.

A process spinning in a tight loop with no syscalls in it will sit there through every SIGTERM you send. Not ignoring them — never seeing them, because it has not been back to the kernel since the first one arrived.

Try this
#include <signal.h>
#include <unistd.h>

void onterm(void) {
  printf("caught\n");
}

int main(void) {
  sigaction(15, "onterm", 0);
  kill(getpid(), 15);
  getppid();
  return 0;
}

Watch: Where the handler's own write lands in the trace, relative to the kill that caused it.

pending-then-delivered

Before you run it

A program installs a handler for SIGKILL and blocks it as well, then kills itself with it.

Predict

What happens?

pending-then-delivered

A bit, not a counter

Pending signals are a set. Sending SIGTERM twice while it is blocked delivers one, because the second found the bit already set and did nothing.

So a handler that counts how many times it ran is not counting how many were sent. A supervisor that reaps one child per SIGCHLD will leave zombies the moment two children exit close together — which is why the correct shape is a loop with WNOHANG until it says there are no more.

Try this
#include <signal.h>
#include <unistd.h>

void onterm(void) {
  printf("x");
}

int main(void) {
  sigaction(15, "onterm", 0);
  sigprocmask("SIG_BLOCK", 15);
  kill(getpid(), 15);
  kill(getpid(), 15);
  sigprocmask("SIG_UNBLOCK", 15);
  getppid();
  printf("\n");
  return 0;
}

Watch: How many x's. Two signals were sent.

pending-then-delivered

Trace

Run one of the programs on the left.

Everything you do here stays in this browser.Part of liter8.sh