I have been using the words program and process almost interchangeably. They are closely related, but Linux gives me a useful reason not to treat them as the same thing.
The distinction I am keeping in my head is:
A program is something that can be executed. A process is a live execution context that executes a program.
That sounds almost pedantic until I start looking at what the operating system actually has to manage.
A file is not yet a running thing
Take a program such as sleep.
There is an executable available on the filesystem. Nothing particularly interesting has to be happening merely because that executable exists.
Run:
sleep 60
and Linux now has something else to deal with: a process.
That running process has an identity and state that the executable file itself does not have. Linux assigns processes process IDs, or PIDs, and uses those identifiers throughout the system to refer to particular processes. A process also exists within an execution environment containing things such as its address space, credentials and other operating-system-managed resources.
GNU’s C Library documentation puts the relationship neatly: a process executes a program, and several processes can independently execute the same program.
That gives me a better model:
program
│
│ executed
▼
process
But the important bit is that this relationship is not one-to-one.
One program, several processes
I can make that visible from Bash.
sleep 60 &
first_pid=$!
sleep 60 &
second_pid=$!
printf 'first: %s\n' "$first_pid"
printf 'second: %s\n' "$second_pid"
I might see something along the lines of:
first: 4217
second: 4218
Both processes are executing sleep, but they are not the same process.
They have different PIDs.
GNU sleep simply delays for the period requested, which makes it convenient here because the processes remain alive long enough to inspect them.
On Linux, I can also look inside /proc.
ls -l "/proc/$first_pid/exe"
ls -l "/proc/$second_pid/exe"
Both symbolic links point to the same executable in this experiment, even though the processes have different identities.
Linux exposes a numerical directory under /proc for each running process, and /proc/<pid>/exe provides a symbolic link to the pathname of the program being executed by that process.
So the picture is really:
┌── process 4217
│
sleep executable ────┼── process 4218
│
└── process 4219
One program. Several executions of it. Several processes.
That is much more useful than thinking of the program itself as somehow becoming “the process”.
A PID belongs to the process
Another distinction clicked here.
The PID is not an identifier for the program.
It identifies a particular process.
If I run sleep again tomorrow, the executable might be exactly the same file, but the operating system will create another process and assign that process an appropriate PID.
Linux’s process documentation describes each process as having a process ID, and also records relationships such as the parent process ID, or PPID. getpid() returns the PID of the calling process while getppid() returns its parent’s PID.
This also explains why tools such as kill operate on PIDs. I am not asking Linux to terminate “the sleep program”. I am identifying a particular running process.
That distinction matters.
I could have several instances of the same program running and terminate one while leaving the others alone.
exec makes the distinction even clearer
There is a slightly stranger detail that makes the program/process separation much harder to unsee.
On Unix-like systems, the exec family of operations can replace the program being executed by an existing process.
Linux’s execve() documentation is unusually explicit about this: execve() does not create a new process. The calling process begins executing a new program, while attributes including its PID remain unchanged.
Conceptually:
process 4217
│
├── executing program A
│
│ exec(...)
▼
└── executing program B
PID: 4217
The program changed.
The process did not become a newly created process merely because a different program image was loaded.
This is also part of the traditional Unix process-creation model: a new child process can be created and that child can subsequently use an exec operation to run another program.
I don’t need all the mechanics of fork() and exec() yet, but this gives me a useful clue about why Unix systems talk about them separately.
Creating a process and choosing which program that process executes are related operations, but they are not conceptually identical.
The mental model I’m keeping
For now:
PROGRAM
───────
Code/instructions capable of being executed.
│
│ execute
▼
PROCESS
───────
A live execution context managed by the operating system.
Has an identity such as a PID and carries runtime state
and resources while executing a program.
And crucially:
one program ──► many processes
When documentation talks about process IDs, signals, parent processes, address spaces or process state, it is talking about the running entities the operating system is managing, not simply the executable files sitting on disk.
That seems like a useful distinction to get right before wandering any further into /proc.
References and further reading
Linux man-pages: execve(2)
Particularly useful for the subtle point that execve() replaces the program executed by an existing process rather than creating another process.
Read execve(2)
Linux man-pages: credentials(7)
Covers process IDs, parent process IDs and other process identifiers.
Read credentials(7)
Linux man-pages: /proc/pid/
Documents Linux’s per-process information exposed through the /proc pseudo-filesystem.
Read proc_pid(5)
Linux man-pages: /proc/pid/exe
Documents the symbolic link to the pathname of the program executed by a process.
Read proc_pid_exe(5)
GNU C Library: Process Creation Concepts
A useful overview of process creation, parent and child processes, process images, fork() and exec().
Read Process Creation Concepts
GNU Coreutils: sleep
Reference for the small sleep experiment used above.
Read the GNU sleep documentation