The exec() family
There is no single function called exec(). Instead, the term refers to a family of functions in libc — execl, execv, execvp etc. that all serve the same purpose: replacing the current process image with a new program1. These functions are all wrappers around the execve(2) system call. They differ in how they accept arguments and whether they search $PATH, but ultimately call execve().
// Core syscall
int execve(const char *path, char *const argv[], char *const envp[]);
You provide a full path, an argument vector, and an environment vector2. $PATH is not consulted — that’s the job of higher-level wrappers.
Here’s a breakdown of the most common exec() variants and how they differ:
| Function | Type | Description |
|---|---|---|
execve |
syscall | Replaces current process image. |
execv |
libc | Like execve, but uses environ for the environment. |
execvp |
libc | Like execv, but searches $PATH. |
execvpe |
libc (GNU) | Like execvp, but takes envp explicitly. |
execl |
libc | Like execv, but uses varargs3 instead of argv[]. |
execlp |
libc | Like execvp, but uses varargs. |
execle |
libc | Like execl, but lets you pass envp. |
The exec() family is typically used in combination with the fork() system call4 to create a new process. A program first calls fork() to create a nearly identical child process5. The child then calls execve() (or one of its wrappers) to transform itself into a new program.
-
The process’s memory (code, data, heap, stack) is replaced by that of the new program. However, key properties like the PID, open file descriptors (unless marked
FD_CLOEXEC), and some kernel-managed attributes remain unchanged. ↩︎ -
The environment is a null-terminated array of strings of the form “KEY=value”, like
"PATH=/usr/bin","HOME=/home/johnetc., representing environment variables passed to the new program. ↩︎ -
“Varargs” (variable arguments) means the arguments are passed directly in the function call, not via an array. ↩︎
-
In fact,
fork()is not a syscall, but a libc wrapper around theclone()syscall. ↩︎ -
The child process has a new PID, a different return value from
fork(), and its own copy of the parent’s memory (using copy-on-write). ↩︎