Przejdลบ do treล›ci

๐Ÿงฌ Advanced execve and fork

Understanding the fundamental system calls that power shell execution reveals deep insights into process management and system behavior.

๐Ÿงญ System Call Foundation

Every program execution in Unix-like systems relies on two core system calls:

fork() โ€” Process Duplication

Creates an almost identical copy of the calling process:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <unistd.h>
pid_t pid = fork();

if (pid == 0) {
    // Child process
    // Has copy of parent's memory, file descriptors, etc.
} else if (pid > 0) {
    // Parent process
    // pid contains child's process ID
} else {
    // fork() failed
    perror("fork");
}

execve() โ€” Program Replacement

Replaces current process image with new program:

1
2
3
4
5
#include <unistd.h>
char *argv[] = {"ls", "-l", NULL};
char *envp[] = {"PATH=/bin:/usr/bin", NULL};
execve("/bin/ls", argv, envp);
// Never returns on success

๐Ÿงช Shell Implementation Pattern

This is how shells implement command execution:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Simplified shell command execution
void execute_command(char *command, char **args) {
    pid_t pid = fork();

    if (pid == 0) {
        // Child process
        execvp(command, args);  // Load new program
        perror("exec failed");  // Only reached if exec fails
        exit(1);
    } else if (pid > 0) {
        // Parent process
        int status;
        waitpid(pid, &status, 0);  // Wait for child
    } else {
        perror("fork failed");
    }
}

๐Ÿง  fork() Details

What Gets Copied

Child inherits from parent: - Memory layout (Copy-On-Write) - File descriptors - Signal handlers - Process group ID - Current working directory - Environment variables - Resource limits

What Doesn't Get Copied

  • Process ID (different PID)
  • Parent process ID (becomes original parent's PID)
  • Pending signals
  • Timers
  • Locks

Copy-On-Write Optimization

Modern systems use COW to avoid unnecessary copying:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Demonstrate COW with large memory allocation
python3 -c "
import os
data = bytearray(100 * 1024 * 1024)  # 100MB
data[0] = 42
if os.fork() == 0:
    data[0] = 84  # Only this page gets copied
    print('Child:', data[0])
else:
    print('Parent:', data[0])
"

๐Ÿงช execve() Family

Several variants of exec exist:

Function Path Resolution Args Format Env Source
execl Relative List Global environ
execv Relative Array Global environ
execle Relative List Provided env
execve Absolute Array Provided env
execlp PATH search List Global environ
execvp PATH search Array Global environ

Example Usage

1
2
3
4
5
6
7
8
9
// execl - list arguments
execl("/bin/ls", "ls", "-l", "/tmp", NULL);

// execv - array arguments
char *args[] = {"ls", "-l", "/tmp", NULL};
execv("/bin/ls", args);

// execvp - search PATH
execvp("ls", args);  // Finds ls in PATH

๐Ÿง  Error Handling

Both calls can fail:

fork() Failures

1
2
3
4
5
# Check for fork failure
if [ $(ulimit -u) -le $(ps -e --no-headers | wc -l) ]; then
    echo "Cannot fork: process limit reached" >&2
    exit 1
fi

Common reasons: - Process limit exceeded (EAGAIN) - Out of memory (ENOMEM) - System limits reached

execve() Failures

Only returns on error:

1
2
3
4
exec /nonexistent/command
# Script continues here only if exec failed
echo "Exec failed!" >&2
exit 1

Common reasons: - File not found (ENOENT) - Permission denied (EACCES) - Not executable (EACCES) - Bad executable format (ENOEXEC)


๐Ÿงช Practical Shell Examples

Manual Process Creation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Simulate shell behavior manually
(
    if [ "$$" != "$BASHPID" ]; then
        echo "We're in a subshell"
    fi

    # Execute external command
    exec ls -l
    # This line never executes if exec succeeds
    echo "Exec failed"
) &
wait

Process Tree Visualization

1
2
3
4
5
# Show process hierarchy
pstree -p $$

# Monitor fork activity
strace -e trace=fork,clone,vfork,execve bash -c 'ls'

Resource Limits Impact

1
2
3
4
5
6
7
8
# Set process limit
ulimit -u 10

# Try to fork (will fail)
for i in {1..15}; do
    (sleep 1) &  # Background processes count toward limit
done
wait

๐Ÿง  Advanced Topics

vfork() Optimization

Lightweight alternative to fork():

1
2
3
4
5
pid_t pid = vfork();  // Shares memory space temporarily
if (pid == 0) {
    exec("/bin/ls", args, env);
    _exit(1);  // Must use _exit(), not exit()
}

Used when immediate exec() follows fork().

clone() System Call

More granular control (Linux-specific):

1
clone(function, stack, flags, arg);

Allows fine-tuning of what gets shared between parent and child.


๐Ÿงพ Summary

  • fork() creates process copies with COW optimization
  • execve() replaces process image entirely
  • Shells combine both to execute commands
  • Understand failure modes for robust scripting
  • Use system tools to monitor process creation
  • Know when optimizations like vfork() apply

๐Ÿ‘‰ Continue to: Subshells and Environment