Ch20.8 โ The signal() API
Linux System Programming ยท EmbeddedPathashala
๐ก Topic 8 of 19
๐ฏ Intermediate
๐ป 3 Code Examples
โ Interview Q&A
๐ Key Terms
signal() SIG_DFL SIG_IGN SIG_ERR sighandler_t Return Value Previous Disposition
The signal() System Call
signal() was the original UNIX API for setting signal dispositions. It is simpler than sigaction() but less portable โ its behaviour varies across UNIX implementations. For production code always use sigaction(). But signal() is important to understand because you’ll see it in legacy code everywhere.
In Linux, signal() is actually a glibc wrapper that calls sigaction() internally.
๐ signal() Prototype (Decoded)
The raw prototype looks confusing because it uses function pointers:
#include <signal.h>
/* Raw prototype โ hard to read */
void ( *signal(int sig, void (*handler)(int)) ) (int);
/* Simplified using typedef โ much cleaner */
typedef void (*sighandler_t)(int);
sighandler_t signal(int sig, sighandler_t handler);
Arguments:
| Argument | Meaning |
|---|---|
| sig | Signal number to change disposition for (e.g. SIGINT) |
| handler | New disposition: address of handler function, SIG_DFL, or SIG_IGN |
Return value: The previous disposition of the signal (so you can save and restore it). Returns SIG_ERR on error.
๐ป Code Example 1 โ Install Handler, Save and Restore Old Disposition
/* Install a handler, save old disposition, then restore it
Compile: gcc -o signal_save signal_save.c
Run: ./signal_save */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static void newHandler(int sig)
{
printf("newHandler called for signal %d\n", sig);
}
int main(void)
{
/* sighandler_t is typedef void(*)(int) */
void (*oldHandler)(int);
/* Install newHandler and save the old disposition */
oldHandler = signal(SIGINT, newHandler);
if (oldHandler == SIG_ERR) {
perror("signal");
return 1;
}
printf("newHandler installed. Press Ctrl+C twice.\n");
sleep(5); /* newHandler fires on Ctrl+C here */
/* Restore original disposition (SIG_DFL = terminate) */
if (signal(SIGINT, oldHandler) == SIG_ERR) {
perror("signal restore");
return 1;
}
printf("Old handler restored. Ctrl+C will now terminate.\n");
sleep(5); /* Default action fires on Ctrl+C here */
return 0;
}
๐ป Code Example 2 โ Temporarily Install Handler Then Restore
/* Pattern: temporarily change, do work, restore
Compile: gcc -o temp_handler temp_handler.c */
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
static void tempHandler(int sig)
{
write(1, "Caught during protected work!\n", 30);
}
int main(void)
{
void (*prev)(int);
printf("Starting protected work...\n");
/* Install temporary handler, save old */
prev = signal(SIGINT, tempHandler);
/* === Protected work โ SIGINT caught by tempHandler === */
int i;
for (i = 0; i < 5; i++) {
printf("Protected step %d\n", i + 1);
sleep(1);
}
/* Restore old disposition */
signal(SIGINT, prev);
printf("Protected work done. Ctrl+C is back to default.\n");
sleep(3);
return 0;
}
๐ป Code Example 3 โ Error Handling with signal()
/* Proper error handling when calling signal()
Compile: gcc -o sig_err sig_err.c */
#include <stdio.h>
#include <signal.h>
#include <errno.h>
static void handler(int sig)
{
write(1, "Handler\n", 8);
}
int main(void)
{
/* signal() returns SIG_ERR on failure */
if (signal(SIGINT, handler) == SIG_ERR) {
perror("signal(SIGINT)");
return 1;
}
/* Attempt on SIGKILL โ should fail (SIG_ERR) */
if (signal(SIGKILL, handler) == SIG_ERR) {
/* This always prints because SIGKILL cannot be caught */
perror("signal(SIGKILL)");
}
/* signal() returns SIG_DFL or SIG_IGN if the signal had
one of those as previous disposition */
void (*prev)(int);
prev = signal(SIGUSR1, handler);
if (prev == SIG_DFL)
printf("SIGUSR1 previously had default action\n");
else if (prev == SIG_IGN)
printf("SIGUSR1 was previously ignored\n");
else
printf("SIGUSR1 previously had a custom handler\n");
pause(); /* wait for signal */
return 0;
}
Portability warning: Use sigaction() in real projects. signal() has undefined behaviour for some corner cases (like signals arriving during handler execution) that differ between BSD and System V lineages.
โ Interview Questions
Q1. What does signal() return on success?
It returns the previous disposition of the signal โ either the address of a previously installed handler function, SIG_DFL, or SIG_IGN. This allows you to save and later restore the original disposition.
Q2. Why is signal() considered non-portable?
Its behaviour differs between UNIX implementations (BSD vs System V). For example, in some implementations the handler is reset to SIG_DFL after each invocation; in others it stays installed. sigaction() has well-defined, consistent behaviour across all POSIX systems.
Q3. What is the return value of signal() on error?
It returns SIG_ERR, a special constant. You should check for this and handle the error, typically with perror().
Q4. Can signal() retrieve the current disposition without changing it?
No. signal() always changes the disposition as a side effect of reading it. To retrieve the current disposition without changing it, you must use sigaction() with a NULL act argument.
Q5. What is sighandler_t and why is it useful?
sighandler_t is a typedef for void (*)(int) โ a pointer to a function that takes an int and returns void. It makes the signal() prototype readable. Without it, the prototype is a confusing chain of pointer declarations.
Next Topic โ
Sending Signals with kill() โ How one process signals another
