Skip to content
/linux-syscalls

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

ArchitectureNumberABIEntry point
x86 (i386)37i386sys_kill
x64 (x86_64)62commonsys_kill
ARM64 (aarch64)129sys_kill

Kernel history

Introduced in Linux 1.0.

  1. 1.0

    kill() has been part of Linux since 1.0 with POSIX semantics.

  2. 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)                    = 0

strace 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.

Related syscalls