Every process on a Linux system except the very first one (PID 1) was created by another process. That parent-child chain, and the two system calls behind it, explain most of what you see in ps output.

fork and exec

Creating a new process is actually two separate steps:

  1. fork() - the calling process is duplicated. The new process (the child) is an almost-exact copy of the parent, with its own PID, running the same code from the same point.
  2. exec() - the child replaces its own memory image with a different program entirely. This is how running ls from Bash works: Bash forks itself, and the child immediately execs into ls.

Most of the time you don't call these directly - a shell does it for every command you run - but the model explains why a forked child inherits things like open file descriptors and environment variables (see Environment Variables) from its parent: it briefly was a copy of the parent before exec replaced its code.

PID, PPID, and the process tree

Every process has a PID (process ID) and a PPID (parent process ID) recording who created it. PID 1 is the init system (see systemd and Services), and every other process traces back to it through a chain of parents.

pstree -p
systemd(1)─┬─sshd(892)───sshd(1203)───bash(1204)───pstree(1522)
           ├─cron(701)
           └─nginx(1050)─┬─nginx(1051)
                          └─nginx(1052)
ps -ef | head -5
UID    PID  PPID  C STIME TTY   TIME CMD
root     1     0  0 09:14 ?     00:00:02 /sbin/init
root   701     1  0 09:14 ?     00:00:00 /usr/sbin/cron
root   892     1  0 09:14 ?     00:00:00 sshd: /usr/sbin/sshd
root  1050     1  0 09:14 ?     00:00:00 nginx: master process

Process states

ps and top show a state code for every process:

Code State Meaning
R Running Actively executing or ready to run
S Sleeping Waiting for an event (the common resting state)
D Uninterruptible sleep Waiting on I/O, usually disk - can't even be killed until it returns
T Stopped Suspended, typically by a job-control signal - see Job Control
Z Zombie Has exited, but its parent hasn't yet collected its exit status

Termination and exit status

When a process finishes, it doesn't disappear immediately - it becomes a zombie, retaining just enough information (its exit status) for its parent to retrieve with a wait() call. A well-behaved parent does this promptly; a large number of long-lived zombie processes usually points to a parent process that's buggy or never calling wait().

If a parent exits before its child does, the child is orphaned and gets re-parented to PID 1 (or, on systemd systems, sometimes a designated subreaper), which will reap it once it exits.

The exit status itself is a number 0–255, retrievable from the shell as $? immediately after a command runs - 0 conventionally means success, anything else signals failure, and a specific non-zero value is often used to indicate which failure occurred. See Shell Scripting for using this in practice, and Signals for the other way a process's lifecycle can end - being killed by a signal rather than exiting on its own.