System Calls vs Signals
System Calls (syscalls)
A system call (syscall) is the programmatic way in which a computer program requests a service from the operating system[a] on which it is executed.
Program → Kernel
Purpose: Request services from the kernel
Examples:
// File operations
int fd = open("/tmp/file.txt", O_RDONLY); // syscall
read(fd, buffer, size); // syscall
close(fd); // syscall
// Process operations
pid_t pid = fork(); // syscall
exit(0); // syscall
// Memory
void* ptr = mmap(...); // syscall
// Network
int sock = socket(AF_INET, SOCK_STREAM, 0); // syscall
How it works:
// User space code
write(1, "Hello", 5);
↓
// Triggers software interrupt/trap
syscall instruction (x86-64)
↓
// Switch to kernel mode
Kernel handles the request
↓
// Return to user space
Result returned to program
Signals
Direction: Kernel → Program (or Program → Program via kernel)
Purpose: Notify program of events (interrupts, errors, user actions)
Common Signals:
SIGINT (2) // Ctrl+C - Interrupt
SIGTERM (15) // Termination request (kill command default)
SIGKILL (9) // Force kill (cannot be caught!)
SIGSEGV (11) // Segmentation fault
SIGCHLD (17) // Child process terminated
SIGSTOP (19) // Stop process (cannot be caught)
SIGALRM (14) // Timer expired
SIGUSR1 (10) // User-defined signal 1
SIGUSR2 (12) // User-defined signal 2
How Ctrl+C Works:
User presses Ctrl+C
↓
Terminal driver detects it
↓
Terminal sends SIGINT to foreground process
↓
Kernel delivers signal to process
↓
Process handles it (or terminates by default)
Key Differences
| Aspect | System Calls | Signals |
|---|---|---|
| Direction | Program → Kernel | Kernel → Program |
| Timing | Synchronous | Asynchronous |
| Initiation | Program requests | External event |
| Purpose | Get kernel services | Notify of events |
| Control flow | Blocking/waiting | Interrupts execution |
| Return | Returns value | No return value |
💡 System programs can use system calls (syscalls) to invoke kernel functions from user space.
💡 Signals are used as a one-way asynchronous notification mechanism for a process.
The Ctrl-C handler is an example of a type of communication mechanism in Linux
Signal Handling
Custom Handler:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void handle_sigint(int sig) {
printf("\\nCaught SIGINT (Ctrl+C)! Signal: %d\\n", sig);
// Don't exit - keep running
}
int main() {
// Register signal handler
signal(SIGINT, handle_sigint);
// or more portable:
struct sigaction sa;
sa.sa_handler = handle_sigint;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
while(1) {
printf("Running... (Press Ctrl+C)\\n");
sleep(1);
}
return 0;
}
Ignoring Signals:
signal(SIGINT, SIG_IGN); // Ignore Ctrl+C
signal(SIGTERM, SIG_DFL); // Restore default behavior
Blocking Signals:
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
// Block SIGINT
sigprocmask(SIG_BLOCK, &set, NULL);
// Critical section - SIGINT won't interrupt
// Unblock
sigprocmask(SIG_UNBLOCK, &set, NULL);
Sending Signals
From Command Line:
# Send SIGTERM (polite termination request)
kill 1234
kill -15 1234
kill -SIGTERM 1234
# Send SIGKILL (force kill - cannot be caught)
kill -9 1234
kill -SIGKILL 1234
# Send to all processes in group
killall process_name
# Ctrl+C sends SIGINT
# Ctrl+Z sends SIGTSTP (stop)
# Ctrl+\\ sends SIGQUIT
From Code:
#include <signal.h>
#include <sys/types.h>
// Send signal to specific process
kill(pid, SIGTERM);
// Send to process group
kill(-pgid, SIGINT);
// Send to self
raise(SIGUSR1);
// Send to child processes
kill(0, SIGCHLD); // All in current process group
Signal Safety
Problem: Signal handlers can interrupt code at any point!
// UNSAFE! malloc is not signal-safe
void unsafe_handler(int sig) {
char* buf = malloc(100); // DANGEROUS!
printf("Signal!\\n"); // Also unsafe
free(buf);
}
Signal-Safe Functions:
Only these can be used in signal handlers:
write()(but notprintf())_exit()(but notexit())- Basic system calls
sigaction(),sigprocmask()
// SAFE signal handler
void safe_handler(int sig) {
const char msg[] = "Signal received\\n";
write(STDERR_FILENO, msg, sizeof(msg)-1);
_exit(1); // Not exit()!
}
Real-World Example: Graceful Shutdown
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
volatile sig_atomic_t shutdown_requested = 0;
void handle_shutdown(int sig) {
// Set flag (signal-safe operation)
shutdown_requested = 1;
}
int main() {
// Setup signal handlers
signal(SIGINT, handle_shutdown); // Ctrl+C
signal(SIGTERM, handle_shutdown); // kill command
printf("Server running. Press Ctrl+C to shutdown gracefully.\\n");
while (!shutdown_requested) {
// Do work
printf("Working...\\n");
sleep(1);
// Use syscalls for actual work
// write(), read(), etc.
}
printf("\\nShutting down gracefully...\\n");
// Cleanup: close files, flush buffers, etc.
return 0;
}