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.


  1. 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. ↩︎

  2. The environment is a null-terminated array of strings of the form “KEY=value”, like "PATH=/usr/bin", "HOME=/home/john etc., representing environment variables passed to the new program. ↩︎

  3. “Varargs” (variable arguments) means the arguments are passed directly in the function call, not via an array. ↩︎

  4. In fact, fork() is not a syscall, but a libc wrapper around the clone() syscall. ↩︎

  5. 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). ↩︎