C Standard Library signal.h

Intermediate
⏱️ 12 min read
📚 Updated: Jul 2026
🎯 5 Examples
<signal.h>

What You’ll Learn

<signal.h> lets your program react to signals—asynchronous events like Ctrl+C (SIGINT), illegal memory access (SIGSEGV), or floating-point errors (SIGFPE). You register a handler with signal and can deliver signals yourself with raise.

01

signal

Set handler.

02

raise

Send signal.

03

SIGINT

Ctrl+C.

04

SIG_DFL

Default.

05

sig_atomic

Safe flag.

06

Safe

Minimal handler.

Definition and Usage

A signal interrupts normal execution. The OS (or your own code via raise) delivers an integer signal number. If you installed a handler with signal, that function runs; otherwise the default action applies (often terminate the program).

Handlers run asynchronously—they can fire while your program is inside printf, malloc, or a loop. That is why handlers must be tiny and use only async-signal-safe operations. The standard pattern is to set a volatile sig_atomic_t flag and do real work in main.

💡
Beginner Tip

The old reference called printf inside a signal handler—that works in simple demos but is not safe in production. Examples below use the flag pattern; printing happens in normal code.

📝 Syntax

Include the header:

C
#include <signal.h>

Standard C functions

C
void (*signal(int sig, void (*handler)(int)))(int);
int raise(int sig);
  • signal(sig, handler) — install handler for signal sig; returns previous handler.
  • raise(sig) — send signal sig to the current program; returns 0 on success.

Common signal macros

  • SIGINT — interrupt (Ctrl+C in terminal).
  • SIGTERM — termination request (often from kill).
  • SIGFPE — floating-point exception (e.g. division by zero).
  • SIGILL — illegal instruction.
  • SIGSEGV — invalid memory access.
  • SIGABRT — from abort().

Special handler values

  • SIG_DFL — default handling (restore with signal(sig, SIG_DFL)).
  • SIG_IGN — ignore the signal.
  • SIG_ERR — error return from signal (not a handler).

POSIX extensions (not standard C)

On Linux/macOS you may also see sigaction, sigprocmask, kill, and signal-set functions. They are not required by ISO C signal.h—the old reference mixed them together; this page separates standard C from POSIX.

Headers and linking

  • #include <signal.h> — no special link flag on most platforms.
  • Compile: gcc program.c -std=c11 -o program

⚡ Quick Reference

CallPurposeExample
signal(SIGINT, fn)Catch Ctrl+CCustom handler
signal(SIGINT, SIG_IGN)Ignore interruptCannot Ctrl+C out
raise(SIGINT)Self-deliveryTest handler
volatile sig_atomic_tHandler → main flagSafe communication
SIG_DFLReset defaultRestore normal exit
Handler
void h(int s) {
  g_stop = 1;
}

Set flag only

Install
signal(SIGINT, h)

Register

Test
raise(SIGINT)

Fire signal

Check
if (g_stop) break;

In main loop

Examples Gallery

Compile with gcc file.c -std=c11 -o out. Examples use raise() so output is reproducible without pressing Ctrl+C. On a real terminal, SIGINT also arrives from the keyboard.

📚 Getting Started

Safe handler design and testing with raise.

Example 1 — SIGINT with sig_atomic_t Flag (Safe Pattern)

Improved version of the reference: handler sets a flag; main prints the message.

C
#include <signal.h>
#include <stdio.h>

static volatile sig_atomic_t got_sigint = 0;

void handle_sigint(int sig) {
    (void)sig;
    got_sigint = 1;
}

int main(void) {
    if (signal(SIGINT, handle_sigint) == SIG_ERR) {
        printf("Could not install SIGINT handler\n");
        return 1;
    }

    printf("Simulating Ctrl+C with raise(SIGINT)...\n");
    raise(SIGINT);

    if (got_sigint) {
        printf("Caught SIGINT (signal %d) — handler ran safely\n", SIGINT);
    }

    return 0;
}

How It Works

raise(SIGINT) delivers the same signal as Ctrl+C. The handler only sets got_sigint—no printf inside the handler. sig_atomic_t is guaranteed writable from a signal handler.

Example 2 — raise() to Test a Custom Handler

Count how many times a handler runs when signals are raised.

C
#include <signal.h>
#include <stdio.h>

static volatile sig_atomic_t count = 0;

void on_signal(int sig) {
    (void)sig;
    count++;
}

int main(void) {
    signal(SIGINT, on_signal);

    raise(SIGINT);
    raise(SIGINT);
    raise(SIGINT);

    printf("Handler called %d time(s)\n", (int)count);

    return 0;
}

How It Works

raise is the standard way to trigger your own handler during tests. Each call increments the atomic counter from the handler.

📈 Practical Patterns

Ignore signals, restore defaults, and shut down cleanly.

Example 3 — Ignore SIGINT with SIG_IGN

Signal is delivered but your handler is not called—the program keeps running.

C
#include <signal.h>
#include <stdio.h>

static volatile sig_atomic_t caught = 0;

void handler(int sig) {
    (void)sig;
    caught = 1;
}

int main(void) {
    signal(SIGINT, handler);
    raise(SIGINT);
    printf("With handler: caught = %d\n", (int)caught);

    signal(SIGINT, SIG_IGN);
    caught = 0;
    raise(SIGINT);
    printf("With SIG_IGN: caught = %d (still 0)\n", (int)caught);

    return 0;
}

How It Works

SIG_IGN tells the OS to discard the signal. Useful when you temporarily want to block Ctrl+C during a critical section (POSIX sigprocmask is more precise on Unix).

Example 4 — Restore Default with SIG_DFL

After one custom catch, reset to default behavior.

C
#include <signal.h>
#include <stdio.h>

static volatile sig_atomic_t once = 0;

void catch_once(int sig) {
    (void)sig;
    once = 1;
}

int main(void) {
    signal(SIGINT, catch_once);
    raise(SIGINT);
    printf("First SIGINT: custom handler (once=%d)\n", (int)once);

    signal(SIGINT, SIG_DFL);
    printf("Restored SIG_DFL — next SIGINT would use default action\n");
    printf("(not calling raise again — default often terminates)\n");

    return 0;
}

How It Works

SIG_DFL restores normal process behavior. A second SIGINT without a handler typically ends the program—so we do not call raise again in this demo.

Example 5 — Graceful Shutdown Loop

Pattern for long-running programs: work until the flag is set.

C
#include <signal.h>
#include <stdio.h>

static volatile sig_atomic_t keep_running = 1;

void request_stop(int sig) {
    (void)sig;
    keep_running = 0;
}

int main(void) {
    int tick = 0;

    signal(SIGINT, request_stop);

    printf("Working... (simulate Ctrl+C after 3 ticks)\n");

    while (keep_running && tick < 10) {
        tick++;
        printf("  tick %d\n", tick);

        if (tick == 3) {
            raise(SIGINT);  /* stand in for user pressing Ctrl+C */
        }
    }

    printf("Clean shutdown after %d ticks\n", tick);
    return 0;
}

How It Works

On tick 3, raise(SIGINT) clears keep_running. The loop exits and you can flush files and free memory in normal code—not inside the handler.

🚀 Common Use Cases

  • CLI tools — catch SIGINT for graceful Ctrl+C shutdown.
  • Servers — handle SIGTERM from process managers (Docker, systemd).
  • Crash signals — log and exit on SIGSEGV (advanced; often use backtrace libraries).
  • Timeouts — combine with alarms or timers (often POSIX alarm).
  • Testingraise to verify handler wiring in unit tests.
  • Child processes — POSIX kill sends signals to other PIDs (not standard C).

🧠 How signal.h Works

1

Install handler

signal(SIGINT, handler) registers your function.

Register
2

Signal arrives

OS or raise delivers the signal asynchronously.

Deliver
3

Handler runs

Minimal work—set sig_atomic_t flag, return quickly.

Handle
=

Main responds

Normal code sees the flag and shuts down or logs safely.

📝 Notes

  • Only async-signal-safe functions belong in handlers (_exit, signal, raise, etc.)—not printf or malloc.
  • Use volatile sig_atomic_t for flags shared with handlers.
  • signal() behavior on repeated signals varies by platform; POSIX sigaction is more reliable.
  • Handlers may interrupt system calls, returning EINTR—retry if needed.
  • Default action for many signals is process termination.
  • Windows C runtime supports a subset of signals; behavior differs from Unix terminals.

⚡ Optimization

Signal handling overhead is negligible for occasional events like Ctrl+C. Avoid installing handlers you do not need. For high-frequency timing, prefer polling or threads rather than flooding the process with signals.

Conclusion

<signal.h> connects your program to OS-level events. Use signal to register handlers, raise to test them, and SIG_DFL / SIG_IGN for default or ignored behavior. Keep handlers minimal; do real work in main.

For deeper Unix programming, learn POSIX sigaction and sigprocmask. Pair with <setjmp.h> only when jumping from signal contexts with sigsetjmp (POSIX).

💡 Best Practices

✅ Do

  • Set a volatile sig_atomic_t flag in handlers
  • Check signal() for SIG_ERR
  • Use raise to test handlers in development
  • Flush and close resources after seeing the stop flag
  • Prefer sigaction on POSIX for production servers

❌ Don’t

  • Call printf, malloc, or exit in handlers
  • Assume kill / sigaction are standard C
  • Run long or complex logic inside signal handlers
  • Ignore EINTR from interrupted syscalls without a plan
  • Rely on undefined behavior after SIGSEGV in the handler

Key Takeaways

Knowledge Unlocked

Five things to remember about signal.h

Signal handling in C, explained simply.

5
Core concepts
📚 02

raise

Send.

Test
📈 03

SIGINT

Ctrl+C.

Common
📄 04

sig_atomic

Safe flag.

Pattern
🌐 05

Minimal

Handler.

Safety

❓ Frequently Asked Questions

signal.h declares the standard C signal interface: signal() to install a handler for a signal number, raise() to send a signal to the current process, and macros like SIGINT, SIGFPE, SIG_DFL, and SIG_IGN. Signals are asynchronous notifications (e.g. Ctrl+C, divide-by-zero).
signal(sig, handler) registers what happens when signal number sig arrives. raise(sig) delivers signal sig to the calling program right now—useful for testing handlers or reporting errors as signals.
Avoid it. printf is not guaranteed async-signal-safe and can deadlock if interrupted while holding a lock. The safe pattern: set a volatile sig_atomic_t flag in the handler, then check the flag in your main loop and print there.
Special handler values passed to signal(). SIG_DFL restores default behavior (often terminate the process). SIG_IGN ignores the signal. Use them instead of a custom function when you want default or no action.
sigaction, sigprocmask, and kill are POSIX extensions—not part of standard C signal.h. They offer more control on Linux/macOS. Standard C programs use signal() and raise(); production Unix code often prefers sigaction over signal().
SIGINT is the interrupt signal, usually sent when the user presses Ctrl+C in a terminal. Default action is to terminate the process. You can catch it with signal(SIGINT, handler) for graceful shutdown.
Did you know?

Before threads were common, signals were how Unix daemons learned to reload config or shut down. Today servers still catch SIGTERM, but most application logic belongs in worker threads—with signal handlers only flipping a flag so the main thread can coordinate an orderly exit.

Explore C Standard Library Headers

Continue with stdarg.h or browse the library index.

Standard Library Index →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful