# How Process Replacement Differs Between Unix and Windows in llmfit-python

> Understand process replacement differences in llmfit-python between Unix and Windows. Learn how Unix uses os.execv for PID preservation while Windows uses subprocess.run.

- Repository: [Alex Jones/llmfit](https://github.com/AlexsJones/llmfit)
- Tags: internals
- Published: 2026-09-11

---

**llmfit-python uses `os.execv` to perform true process replacement on Unix-like systems, preserving the original PID, while on Windows it falls back to `subprocess.run`, spawning the binary as a child process and changing the PID.**

The `llmfit-python` wrapper in the AlexsJones/llmfit repository serves as a cross-platform launcher for the compiled `llmfit` binary. Understanding how **process replacement** works under the hood reveals why Unix systems replace the Python interpreter entirely while Windows maintains an intermediary parent process.

## Unix-like Systems: True Process Replacement with os.execv

On Linux, macOS, and other Unix-like platforms, the wrapper invokes `os.execv` to transform the running Python process into the `llmfit` binary. This system call, which maps to `execve` at the kernel level, swaps the process image in-place without creating a new process entry.

The implementation appears in [`llmfit-python/src/llmfit/__main__.py`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-python/src/llmfit/__main__.py) at line 22:

```python
os.execv(bin_path, args)  # Process is replaced; never returns

```

Because `execv` reuses the existing process slot, the **PID remains unchanged** and no Python interpreter remains resident in memory. The binary inherits the exact command-line arguments passed to the wrapper, providing seamless redirection.

## Windows: Child Process Spawning with subprocess.run

Windows lacks a native `execve` equivalent that replaces the current process image, so the wrapper imports `subprocess` and calls `subprocess.run()` instead. This approach creates a new child process to host `llmfit.exe`, leaving the original Python process alive to monitor execution.

The Windows-specific branch occupies lines 13-20 in [`llmfit-python/src/llmfit/__main__.py`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-python/src/llmfit/__main__.py):

```python
if sys.platform == "win32":
    completed = subprocess.run(args, check=False)
    sys.exit(completed.returncode)

```

This method means the **PID changes** when the binary launches, and the Python process consumes resources until the child exits. The wrapper explicitly forwards the child's return code via `sys.exit()` to maintain predictable error handling.

## Cross-Platform Binary Resolution

Before invoking either method, the wrapper determines which binary to execute. The `find_llmfit_bin()` function in [`llmfit-python/src/llmfit/__init__.py`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-python/src/llmfit/__init__.py) (lines 27-33) returns `llmfit.exe` on Windows and `llmfit` on other platforms, ensuring the correct executable format for the target operating system.

## Practical Usage Examples

To run the wrapper across any platform:

```bash
python -m llmfit --help

```

### Unix Process Replacement

To manually reproduce the Unix behavior:

```python
import os
import sys

binary = "/usr/local/bin/llmfit"
args = [binary] + sys.argv[1:]
os.execv(binary, args)  # Current process becomes llmfit

```

### Windows Process Spawning

To manually reproduce the Windows behavior:

```python
import subprocess
import sys

binary = "path/to/llmfit.exe"
args = [binary, "--version"]
proc = subprocess.run(args, check=False)
sys.exit(proc.returncode)

```

## Summary

- **Unix systems** use `os.execv` in [`llmfit-python/src/llmfit/__main__.py`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-python/src/llmfit/__main__.py) (line 22) to replace the Python process entirely, preserving the PID and eliminating interpreter overhead.
- **Windows systems** use `subprocess.run` (lines 13-20) to spawn a child process, resulting in a new PID and retaining the parent Python process until completion.
- The `find_llmfit_bin()` function in [`llmfit-python/src/llmfit/__init__.py`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-python/src/llmfit/__init__.py) (lines 27-33) resolves platform-specific binary names (`llmfit.exe` vs `llmfit`).
- Both approaches forward command-line arguments and exit codes identically, ensuring consistent behavior despite differing process architectures.

## Frequently Asked Questions

### Does the PID change when running llmfit-python on Windows?

Yes. On Windows, `llmfit-python` spawns a child process via `subprocess.run`, creating a new PID for the `llmfit.exe` binary while the original Python process remains alive. On Unix systems, `os.execv` replaces the current process image, preserving the original PID.

### Why can't Windows use os.execv like Unix systems?

Windows does not provide an `execve` system call that replaces the current process image without creating a new process. The Windows process model requires spawning a new executable as a separate child process, which is why the wrapper falls back to `subprocess.run` when `sys.platform == "win32"` is detected.

### How does the wrapper know which binary to execute?

The `find_llmfit_bin()` function in [`llmfit-python/src/llmfit/__init__.py`](https://github.com/AlexsJones/llmfit/blob/main/llmfit-python/src/llmfit/__init__.py) checks the platform at runtime, returning `llmfit.exe` for Windows and `llmfit` for Unix-like systems. This ensures the correct executable format is passed to either `os.execv` or `subprocess.run`.

### Will the exit code propagate correctly on both platforms?

Yes. The Unix implementation inherits the exit code naturally through process replacement, while the Windows implementation explicitly captures the child's return code from `subprocess.run` and passes it to `sys.exit()`, ensuring identical exit behavior across operating systems.