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
| Signal | Can be caught/ignored? | Typical use |
|---|---|---|
| SIGTERM | Yes | Ask a process to shut down gracefully |
| SIGKILL | No, never | Force-terminate a process that ignored SIGTERM |
| SIGHUP | Yes | Traditionally "terminal disconnected"; often repurposed to reload config |
systemd Type= | Considered "started" when |
|---|---|
simple (default) | The main process is forked off |
forking | The parent process exits after setup completes |
oneshot | The main process exits |
notify | The service calls sd_notify() to report readiness |
Syntax
kill -TERM 4821 # ask the process to shut down gracefully
kill -KILL 4821 # force-terminate, cannot be caught or ignored
systemctl restart myapp.serviceExamples
# 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-failureSIGKILL 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=simplefor a service whose own startup takes real time, causing dependent units to start before it's actually ready to handle requests. - Setting
Restart=alwayson a service that's crash-looping due to a real bug, masking the failure instead of surfacing it. - Mixing BSD-style and UNIX-style
psoptions 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=notifyavoids 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=notifyfor services that support it, over guessing atsimpleorforking, since it's the only mode based on the service's own explicit signal. - Set
Restart=on-failureas the default for long-running services rather thanalways, so a persistently crashing service surfaces as a visible failure instead of restarting forever. - Pick one
psoption convention (BSD or UNIX-style) and use it consistently, rather than mixing them and reasoning about output columns case by case.