Cloud Tech by Victor
DevOpsIntermediate

Linux Process Management & systemd

Why SIGKILL can't be caught or ignored the way SIGTERM can, how systemd's Type= actually decides when a service counts as "started," and the difference between the ps command's BSD and UNIX option styles.

Updated 2026-07-243 min read

Overview

Process control on Linux happens through signals, and the two that matter most are opposites in one specific way: SIGTERM is a request a process can catch and handle for graceful shutdown, while SIGKILL and SIGSTOP cannot be caught, blocked, or ignored by any process, ever, which is exactly why SIGKILL is the tool of last resort, not the default one. systemd builds service lifecycle management on top of process behavior via its Type= setting, simple considers a unit started the instant the process forks, forking waits for the traditional daemon parent-exits pattern, and notify waits for the service to explicitly report its own readiness, three genuinely different answers to "when is this actually up." ps layers two historical option conventions, UNIX-style and BSD-style, onto the same command, and they change both the selected processes and the displayed columns.

Quick Reference

SignalCan be caught/ignored?Typical use
SIGTERMYesAsk a process to shut down gracefully
SIGKILLNo, neverForce-terminate a process that ignored SIGTERM
SIGHUPYesTraditionally "terminal disconnected"; often repurposed to reload config
systemd Type=Considered "started" when
simple (default)The main process is forked off
forkingThe parent process exits after setup completes
oneshotThe main process exits
notifyThe service calls sd_notify() to report readiness

Syntax

bash
kill -TERM 4821      # ask the process to shut down gracefully
kill -KILL 4821      # force-terminate, cannot be caught or ignored
systemctl restart myapp.service

Examples

ini
# myapp.service - on-failure restart is the recommended default
# for a long-running service, not "always" or "no".
[Service]
Type=notify
ExecStart=/usr/bin/myapp
Restart=on-failure

SIGKILL skips all cleanup logic

A process killed with SIGKILL gets no chance to close files, release locks, or flush buffers, the kernel terminates it directly with no handler ever running. Always send SIGTERM first and give the process a real chance to exit cleanly before escalating.

Visual Diagram

Common Mistakes

  • Reaching for kill -9 (SIGKILL) as a first move instead of SIGTERM, skipping cleanup logic that mattered.
  • Using Type=simple for a service whose own startup takes real time, causing dependent units to start before it's actually ready to handle requests.
  • Setting Restart=always on a service that's crash-looping due to a real bug, masking the failure instead of surfacing it.
  • Mixing BSD-style and UNIX-style ps options without understanding that each convention changes both which processes are selected and which columns are shown.

Performance

  • Signal delivery itself is essentially instantaneous at the kernel level; the real time cost is however long the receiving process's own handler takes to run before it exits.
  • Type=notify avoids systemd either understating or overstating a service's readiness, which matters for dependency-ordered startups where a premature "started" signal causes real request failures downstream.

Best Practices

  • Always attempt SIGTERM before SIGKILL, and give a service a real, bounded timeout to shut down cleanly before escalating.
  • Choose Type=notify for services that support it, over guessing at simple or forking, since it's the only mode based on the service's own explicit signal.
  • Set Restart=on-failure as the default for long-running services rather than always, so a persistently crashing service surfaces as a visible failure instead of restarting forever.
  • Pick one ps option convention (BSD or UNIX-style) and use it consistently, rather than mixing them and reasoning about output columns case by case.

Interview questions

Why does sending SIGKILL to a stuck process work when SIGTERM doesn't, and what does that cost you?

SIGTERM asks a process to terminate but can be caught by a signal handler, letting the process run its own cleanup logic (closing files, flushing buffers, releasing locks) before actually exiting, or in a broken process, being caught and never acted on at all. SIGKILL cannot be caught, blocked, or ignored under any circumstances, the kernel terminates the process directly, which is why it works on a process SIGTERM couldn't reach. The cost is that none of that cleanup logic runs, a database connection isn't closed cleanly, a temp file isn't removed, a lock isn't released, so SIGKILL is a last resort after SIGTERM has been given a real chance to work, not a default first move.

In a systemd unit, what is the practical difference between Type=simple and Type=forking, and why does that distinction matter for dependency ordering?

With Type=simple, systemd considers the unit started the moment the main process is forked off, it does not wait for the application to finish its own initialization, so anything depending on that unit might start before the service is actually ready to handle requests. Type=forking expects the traditional daemon pattern, the initial process forks and exits once it judges its own startup complete, so systemd marks the unit started as soon as that original process exits successfully, while the actual daemon keeps running as a separate, now-orphaned process. That only tracks the daemonization handoff, not genuine application readiness, a process can exit believing setup is done while it is still finishing initialization in the background, so Type=forking is a better signal than Type=simple but still not a readiness guarantee. Type=notify is the one that actually is readiness-safe: the service explicitly calls sd_notify to tell systemd exactly when it's ready, rather than systemd inferring readiness from process exit behavior at all.

What is the practical difference between ps -ef and ps aux, and why do they show different columns for the same processes?

`-ef` is UNIX-style syntax and `aux` is BSD-style syntax for the same underlying command, and they weren't designed as one consistent interface; mixing them can even be ambiguous depending on other options used. The manual is explicit that BSD-style options change the default output to include process state (STAT) and full command arguments (COMMAND) instead of just the executable name, and BSD-style selection also defaults to showing every process the invoking user owns across all terminals, while UNIX-style selection defaults to processes on the current terminal only. Neither is "more correct," they're two different historical option conventions layered onto the same command, which is why picking one and being consistent about it matters more than which one.

References

ShareXLinkedInReddit
Was this page helpful?
Suggest an improvement