Signals · Section 2
kill(2)
Send a signal to a process or process group.
Signature
#include <signal.h>
int kill(pid_t pid, int sig);- pid
- Target. > 0: specific PID. 0: caller's process group. -1: every process the caller may signal. < -1: process group with the absolute value as PGID.
- sig
- Signal number (SIGTERM = 15, SIGKILL = 9, SIGINT = 2, etc.) or 0 to test for existence/permission without delivery.
Description
kill() sends signal sig to the process identified by pid. Semantics of pid: > 0 sends to that exact PID; 0 sends to every process in the caller's process group; -1 sends to every process the caller has permission to signal (except init); < -1 sends to every process in the process group |pid|. sig == 0 performs no delivery but still checks permissions — the idiomatic way to test whether a PID exists without affecting it. The caller must own the target (same UID/EUID) or hold CAP_KILL. Returns 0 on success, -1 with errno on failure. Modern code prefers pidfd_send_signal() to avoid the PID-reuse race that can cause kill() to hit the wrong process after the original PID was reused.
Architecture mapping
| Architecture | Number | ABI | Entry point |
|---|---|---|---|
| x86 (i386) | 37 | i386 | sys_kill |
| x64 (x86_64) | 62 | common | sys_kill |
| ARM64 (aarch64) | 129 | — | sys_kill |
Kernel history
Introduced in Linux 1.0.
1.0
kill() has been part of Linux since 1.0 with POSIX semantics.
5.1
pidfd_send_signal() was added so callers can refer to a target by a pidfd (obtained from clone(CLONE_PIDFD), pidfd_open(), or /proc/<pid>) — eliminating the PID-reuse race that can cause kill() to deliver to the wrong process if the original target exited and its PID was recycled.
seccomp & containers
Docker default profile
Allowed
Podman default profile
Allowed
kill() is allowed by Docker / Podman default profiles. For most container workloads, signal-sending is needed only between sidecar and main, or between init and workers — which means pid == 0 (process group) and pid == self. Restricting to self via argument filtering blocks lateral signalling within the container's PID namespace, which is a useful hardening if the workload's threading model permits.
libseccomp
// Allow self-signalling only (pid == 0 or pid == getpid())
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(kill),
1, SCMP_A0(SCMP_CMP_EQ, 0));strace example
$ strace -e kill bash -c 'kill -USR1 $$ &'
kill(12834, SIGUSR1) = 0strace decodes sig symbolically (SIGTERM, SIGKILL). A 'kill(target, 0) = 0' followed by 'kill(target, SIGTERM) = 0' is the canonical 'is it alive then please stop' pattern of orchestrators. The race-free modern alternative shows up as pidfd_send_signal() — worth knowing when reading recent code.
Security & observability
kill() to PID 1 inside a container is a common DoS escape — a compromised process can SIGKILL the container init to crash the pod. Most runtimes set the init's SUBREAPER/PID-namespace-root so SIGKILL is filtered, but it's worth verifying for your runtime. Beyond DoS, kill() rarely appears in attacks — it's a permission-checked operation against the same userns, so cross-tenant abuse is naturally bounded. eBPF tracepoint sys_enter_kill captures pid and sig; rules on SIGKILL to PID 1 or to known sidecar PIDs are high-signal in container EDR products.
Errors
- EINVAL
- sig is not a valid signal number.
- EPERM
- The caller does not have permission to signal the target — different real UID and EUID, or missing CAP_KILL, or target is in a different user namespace.
- ESRCH
- No process or process group with the given pid exists. With sig == 0 this is the canonical 'process is gone' indicator.