You write a graceful shutdown handler in Node.js, Python, or Go. You catch SIGTERM and SIGINT, close your database connections, flush open files, and exit cleanly.
Then someone runs kill -9 <pid> (or Kubernetes kills your container after a timeout).
Your cleanup handlers never run. Your open files aren’t flushed. Your database connection drops abruptly.
Why? Why can’t a process catch, block, or ignore SIGKILL? What actually happens inside the Linux kernel when signal 9 is delivered?
1. How Regular Signals Work vs SIGKILL
To understand why SIGKILL is unstoppable, you first have to understand how standard signals (like SIGTERM or SIGINT) work.
Every Linux process has a struct task_struct in kernel space. Inside that struct lives a signal handler table (struct sighand_struct):
[User Space Process]
▲
│ 3. Kernel switches execution to your custom handler
[Linux Kernel]
▲
│ 2. Kernel checks sighand_struct -> points to your function
sys_kill(pid, SIGTERM)
│ 1. Signal posted to pending queue
Enter fullscreen mode Exit fullscreen mode
When you call signal(SIGTERM, handler) or signal.signal(signal.SIGTERM, my_func):
- You register a memory address pointing to your custom function inside your process’s virtual memory.
- When
SIGTERMarrives, the kernel queues it in the process’s pending signal mask. - On the next context switch back to user space, the kernel sets up a stack frame and redirects the CPU to execute your handler.
Your application code is in charge of responding.
2. The SIGKILL Exception in Kernel Space
SIGKILL (signal 9) and SIGSTOP (signal 19) operate on a completely different path.
When kill(pid, 9) is invoked, it enters the kernel via sys_kill() and reaches complete_signal() in kernel/signal.c.
The kernel checks the signal number:
/* Simplified Linux Kernel Logic in kernel/signal.c */
if (sig == SIGKILL) {
/* 1. Bypass sighand_struct entirely */
/* 2. Mark the thread group with SIGNAL_GROUP_EXIT */
/* 3. Immediately set task state to TASK_DEAD / exit */
do_group_exit(sig);
}
Enter fullscreen mode Exit fullscreen mode
The key architectural differences:
Feature Standard Signal (SIGTERM / 15)
Force Kill (SIGKILL / 9)
Handling
Process user-space handler runs
Bypasses user space completely
Blockable?
Yes (via sigprocmask)
No (Kernel ignores mask)
Catchable?
Yes (signal() / sigaction())
No (Kernel returns EINVAL)
Cleanup
Flushes buffers, closes DB pools
Instant kernel task destruction
Delivery
Delivered on next return to user-space
Executed immediately by scheduler
3. What Happens to Resources on SIGKILL?
Because user-space code never runs again, developers often worry about resource leaks. Here is what the Linux kernel actually guarantees:
- Virtual Memory Reclaimed: All page tables and virtual memory regions allocated to the process are freed immediately.
-
File Descriptors Closed: The kernel calls
close()on all open file descriptors. Sockets send aFINorRSTpacket to remote endpoints. - Locks Released: POSIX file locks held by the process are automatically released.
What is NOT cleaned up:
- Unwritten User Buffers: Data sitting in application-level buffers (e.g. standard I/O streams not yet flushed to disk) is lost.
-
Temporary Files: Files created on
/tmpwithout an auto-delete wrapper stay on disk. - Database Transactions: In-flight database transactions are aborted by the database server when the socket closes, triggering rollback.
4. Practical Takeaways for Docker and Kubernetes
Understanding SIGKILL explains how container termination lifecycles work:
-
The Grace Period: When Kubernetes stops a pod, it sends
SIGTERMfirst. -
The Countdown: The application has
terminationGracePeriodSeconds(default: 30s) to catchSIGTERM, finish in-flight requests, and exit. -
The Guillotine: If the process is still alive after the grace period, the container runtime sends
SIGKILL.
docker stop -> SIGTERM (15) -> [ 10s grace period ] -> SIGKILL (9)
Enter fullscreen mode Exit fullscreen mode
Golden Rule:
- Always listen for
SIGTERMto perform graceful cleanup. - Never design a system that relies on catching
SIGKILL— by POSIX design and kernel mechanics,SIGKILLbelongs to the operating system, not your application.