Lesson 04intermediateProcesses

Program Execution with exec()

Replacing a process image with exec(). Covers the exec family (execl, execlp, execv, execvp), the fork+exec pattern for running child programs, and a practical calculator example split across parent and child.

execexeclexecvpfork-execprocess-image
1

exec Family Variants

Demonstrating execl, execlp, execv, and execvp — the four main ways to call exec with different argument styles.

basic_exec.c64 lines
/*
 * basic_exec.c — exec family — execl, execlp, execv, execvp variants
 *
 * Key concepts: exec replaces process image, 'l' vs 'v', 'p' for PATH search
 * Compile: gcc -o exec basic_exec.c
 * Run:     ./prog
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>



int main() {
    printf("\n");
    printf("========================================\n");
    printf("  exec family — execl, execlp, execv\n");
    printf("========================================\n\n");
    const int EXAMPLE = 1;

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    }

    if (pid == 0)
    {
        printf("[Child  PID %d] About to call exec — this process image will be REPLACED.\n\n", getpid());
        int status;
        switch (EXAMPLE)
        {
        case 1:
            printf("[Child] Using execl(\"/bin/ls\", \"ls\", \"-l\", NULL)\n");
            printf("        'l' = list arguments, no 'p' = full path required\n\n");
            status = execl("/bin/ls" ,"ls", "-l", NULL);
            break;
        case 2:
            printf("[Child] Using execv(\"/bin/ls\", args)\n");
            printf("        'v' = vector (char* array), no 'p' = full path required\n\n");
            char *args[] = {"ls", "-l", NULL};
            status = execv("/bin/ls", args);
            break;

        default:
            break;
        }
        if (status == -1) {
            perror("[Child] exec failed");
            exit(1);
        }

    }
    else {
        printf("[Parent PID %d] Created child PID %d. Waiting...\n", getpid(), pid);
        wait(NULL);
        printf("[Parent PID %d] Child finished. exec replaced its entire process image.\n\n", getpid());
        printf("========================================\n");
    }

    return 0;
}
Expected Outputclick Run to execute live
total 32
-rwxr-xr-x 1 yaniv yaniv 16312 Aug 31 18:32 basic_exec
-rw------- 1 yaniv yaniv  1447 Aug 31 01:34 basic_exec.c
-rw------- 1 yaniv yaniv  2904 Aug 31 01:34 binary_search_dupes.c
-rw------- 1 yaniv yaniv  1498 Aug 31 01:34 calculator.c
-rw------- 1 yaniv yaniv  1767 Aug 31 01:34 calculator_exec.c

========================================
  exec family — execl, execlp, execv
========================================

Parent process waiting for the child
Parent process finished
2

Calculator (Child Program)

A standalone calculator that reads operands and an operator from argv — designed to be launched via exec.

calculator.c57 lines
/*
 * calculator.c — Calculator child program (used by calculator_exec.c)
 *
 * Key concepts: This is the child program that exec launches
 * Compile: gcc -o calculator calculator.c
 * Run:     ./prog
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>    // for getpid()

int main(int argc, char *argv[]) {
    printf("\n");
    printf("========================================\n");
    printf("  Calculator (exec'd child process)\n");
    printf("========================================\n\n");
    printf("[Calculator PID %d] Launched by parent via exec().\n", getpid());
    printf("[Calculator] Received %d arguments (expected 3: num op num)\n\n", argc - 1);
    if (argc != 4) {
        fprintf(stderr, "Usage: %s <num1> <operator> <num2>\n", argv[0]);
        return 1;
    }

    // Parse arguments
    int num1 = atoi(argv[1]);
    char operator = argv[2][0];
    int num2 = atoi(argv[3]);
    int result = 0;

    // Perform calculation based on the operator
    switch (operator) {
        case '+':
            result = num1 + num2;
            break;
        case '-':
            result = num1 - num2;
            break;
        case '*':
            result = num1 * num2;
            break;
        case '/':
            if (num2 == 0) {
                fprintf(stderr, "Error: Division by zero\n");
                return 1;
            }
            result = num1 / num2;
            break;
        default:
            fprintf(stderr, "Error: Invalid operator '%c'\n", operator);
            return 1;
    }

    printf("[Calculator] %d %c %d = %d\n\n", num1, operator, num2, result);
    printf("========================================\n");
    return 0;
}
Expected Outputclick Run to execute live
========================================
  Calculator child program (used by ca
========================================

Usage: ./calculator <num1> <operator> <num2>
3

Calculator Launcher (Parent)

Parent program that forks a child and uses exec to launch the calculator program, passing arguments through argv.

calculator_exec.c63 lines
/*
 * calculator_exec.c — Parent program that fork+exec's the calculator
 *
 * Key concepts: fork+exec pattern, parent passes args to child via exec
 * Compile: gcc -o calc_exec calculator_exec.c
 * Run:     ./prog
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    printf("\n");
    printf("========================================\n");
    printf("  fork() + exec() — Launch Calculator\n");
    printf("========================================\n\n");
    printf("[Parent PID %d] Will fork, then exec the calculator program.\n\n", getpid());
    const int EXAMPLE = 1;

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        exit(1);
    }

    if (pid == 0) {
        printf("[Child  PID %d] Replacing myself with ./calculator via exec...\n", getpid());
        int status;

        switch (EXAMPLE) {
            case 1: {
                // Using execl: Pass arguments as separate strings
                status = execl("./calculator", "calculator", "5", "+", "3", NULL);
                break;
            }
            case 2: {
                // Using execv: Pass arguments as a char array
                char *args[] = {"calculator", "8", "*", "4", NULL};
                status = execv("./calculator", args);
                break;
            }
            default:
                fprintf(stderr, "Invalid EXAMPLE value\n");
                exit(1);
        }

        if (status == -1) {
            perror("exec failed");
            exit(1);
        }
    } else {
        // Parent process
        printf("[Parent PID %d] Waiting for child PID %d to complete...\n", getpid(), pid);
        wait(NULL);
        printf("[Parent PID %d] Child finished. The calculator ran in a separate process.\n\n", getpid());
        printf("========================================\n");
    }

    return 0;
}
Expected Outputclick Run to execute live
========================================
  Parent program that fork+exec's the 
========================================

Child process executing the calculator program...

========================================
  Parent program that fork+exec's the 
========================================

Parent process waiting for the child to complete...
Parent process finished.
exec failed: No such file or directory
4

Binary Search Utility

A binary search implementation that handles duplicates — a review exercise combining C fundamentals with algorithms.

binary_search_dupes.c93 lines
Expected Outputclick Run to execute live
========================================
  Binary search with duplicate handlin
========================================

Test 1: Count of 2 = 3 (Expected: 3)
Test 2: Count of 1 = 6 (Expected: 6)
Test 3: Count of 5 = 1 (Expected: 1)
Test 4: Count of 3 = 3 (Expected: 3)
Test 5: Count of 10 = 0 (Expected: 0)

Open Full Terminal

Get a Linux terminal with all 4 files from this lesson pre-loaded. Compile, run, and experiment freely.