run a program in background with execvp system call in c
Categories:
Running Programs in the Background with execvp
in C

Learn how to use the execvp
system call in C to execute external programs, specifically focusing on techniques to run them in the background on Linux/POSIX systems.
Executing external programs from within a C application is a common requirement in system programming. The execvp
system call, part of the exec
family, provides a powerful way to replace the current process image with a new one. While execvp
itself doesn't directly 'background' a process, it's a crucial component in achieving this functionality when combined with process creation mechanisms like fork()
.
Understanding execvp
and the exec
Family
The exec
family of functions (e.g., execl
, execle
, execlp
, execv
, execve
, execvp
) replaces the current process image with a new process image. This means that after a successful exec
call, the original program's code is no longer running; it's been entirely replaced by the new program. If execvp
succeeds, it never returns. If it fails, it returns -1 and sets errno
.
execvp
is particularly useful because it searches for the executable in the directories specified by the PATH
environment variable, similar to how a shell executes commands. It takes the program name and an array of string arguments, where the first argument is typically the program name itself.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
char *args[] = {"ls", "-l", NULL};
printf("Attempting to execute 'ls -l'\n");
execvp("ls", args);
// If execvp succeeds, this line is never reached
perror("execvp failed"); // Only reached if execvp fails
return 1;
}
Basic usage of execvp
to execute the ls -l
command.
Running a Program in the Background
To run a program in the background using execvp
, you must first create a new process using fork()
. The fork()
system call creates a child process that is an exact copy of the parent process. The key is to call execvp
only in the child process. The parent process can then continue its execution, effectively 'backgrounding' the child process.
To prevent the child process from becoming a zombie upon termination, the parent process typically needs to wait()
for it. However, for true background execution, we often want the parent to not wait. This can be achieved by having the parent immediately return or by having the child process detach itself from the terminal. A common pattern for backgrounding is to fork()
twice, creating a grandchild process that is then execvp
'd, and the intermediate child exits immediately, allowing the original parent to continue without waiting.
flowchart TD A[Parent Process Start] --> B{fork()};; B -->|Parent| C[Parent Continues];; B -->|Child| D{fork()};; D -->|Child (Intermediate)| E[Child Exits];; D -->|Grandchild| F[execvp(program)];; F --> G[Background Program Runs];; G --> H[Background Program Terminates];; style E fill:#f9f,stroke:#333,stroke-width:2px style G fill:#ccf,stroke:#333,stroke-width:2px
Process flow for background execution using double fork()
.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
void run_in_background(char *program, char *args[]) {
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child process
// Detach from controlling terminal (optional, but good practice for daemons)
setsid();
// Redirect standard I/O to /dev/null or log files
// For simplicity, we'll just close them here
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// Execute the program
execvp(program, args);
// If execvp returns, it means an error occurred
perror("execvp failed in child");
_exit(EXIT_FAILURE); // Use _exit in child after fork
} else {
// Parent process
printf("Child process (PID: %d) launched in background.\n", pid);
// Parent does NOT wait for the child
}
}
int main() {
char *program_to_run = "sleep";
char *program_args[] = {"sleep", "5", NULL};
printf("Main program starting.\n");
run_in_background(program_to_run, program_args);
printf("Main program continuing its work.\n");
sleep(1); // Give some time for the background process to start
printf("Main program finished.\n");
return 0;
}
C code demonstrating how to run a program in the background using fork()
and execvp
.
/dev/null
or log files. Otherwise, the background process might try to read from or write to the terminal, leading to unexpected behavior or blocking.Handling Zombie Processes and SIGCHLD
When a child process terminates, it enters a 'zombie' state until its parent calls wait()
or waitpid()
. If the parent doesn't wait, the zombie process will persist, consuming system resources (though minimal). For true backgrounding without the parent waiting, you can either:
- Double
fork()
: As shown in the Mermaid diagram, the intermediate child exits immediately, making the grandchild an orphan. Theinit
process (PID 1) then adopts the grandchild and automatically reaps it when it terminates. - Handle
SIGCHLD
: The parent can install a signal handler forSIGCHLD
. When a child terminates, this signal is sent to the parent. The signal handler can then callwaitpid()
withWNOHANG
to reap the child without blocking the parent's main execution flow.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
void sigchld_handler(int signo) {
int status;
pid_t pid;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
printf("Reaped child process %d\n", pid);
}
}
void run_in_background_with_sigchld(char *program, char *args[]) {
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
exit(EXIT_FAILURE);
} else if (pid == 0) {
// Child process
setsid();
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
execvp(program, args);
perror("execvp failed in child");
_exit(EXIT_FAILURE);
} else {
// Parent process
printf("Child process (PID: %d) launched in background with SIGCHLD handler.\n", pid);
}
}
int main() {
// Register SIGCHLD handler
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, 0) == -1) {
perror("sigaction failed");
exit(EXIT_FAILURE);
}
char *program_to_run = "sleep";
char *program_args[] = {"sleep", "3", NULL};
printf("Main program starting.\n");
run_in_background_with_sigchld(program_to_run, program_args);
printf("Main program continuing its work.\n");
sleep(5); // Parent sleeps longer than child to allow SIGCHLD to be caught
printf("Main program finished.\n");
return 0;
}
Using a SIGCHLD
handler to automatically reap background processes.
waitpid()
is generally safe to call within a SIGCHLD
handler.