← Computing foundations · Chapter 2

After a program starts: processes, memory, and files

Distinguish process drafts, virtual memory, and saved data, including paths, failed saves, crash durability, and version conflicts.

After this guide, you can

Separate in-memory edits from persistence and check failed saves and stale-version overwrites.

An editor's open document, a string inside a running program, and the file on disk can all have different contents at the same moment. Processes, memory, and files explain why a screen can show an edit that disappears after restart, and why an in-memory variable is not a reliable task record.

Consider a note application: someone edits a draft, saves it, closes the app, and starts it again. Predictable behavior requires defining who owns the draft, who owns the saved version, and what happens when saving fails.

From system startup to program startup

A computer usually starts through firmware, a bootloader, the kernel, and user-space initialization. Details vary by platform. The kernel manages execution, memory, devices, and other resources. User programs request operations through system calls. Starting an application establishes a process environment rather than merely copying source text into memory.

A process has an address space and resources. Threads execute within it, and a scheduler decides when they run. Threads generally share their process's address space, so changes to shared objects need coordination. Ordinary variables in different processes do not become shared merely because their names match.

Processes can communicate through pipes, sockets, or explicitly shared memory. These boundaries require design. Starting a second note process does not automatically transfer an unsaved string from the first process into the second.

A virtual address is not a fixed block of RAM

Page tables and related mechanisms map virtual addresses to physical resources. This supports isolated process address spaces and uses such as file mappings. An existing virtual address does not prove its data is currently resident in physical memory.

A page may not yet have a physical mapping, may refer to file contents, or may involve swapping. Access can cause a page fault. The outcome depends on whether the mapping is valid and whether the system can satisfy the access. A page fault is not always an application error, while waiting longer cannot repair an invalid access.

When investigating memory problems, distinguish virtual address-space size, resident memory, allocator-retained space, and objects still referenced by the application. One rising or falling number cannot establish a leak. Use object lifetimes, process metrics, and a reproducible workload together.

File names, open files, and contents

A filesystem uses directories and other structures to associate names with file objects and metadata. Opening a file gives a process a descriptor or platform-specific handle for operations such as reading and writing. A path locates an object; a handle refers to an opened object. They serve different roles.

Renaming a file does not have one universal relationship to whether an existing handle can continue reading. Permissions, sharing rules, and filesystem behavior depend on the platform. The working directory is not fixed either. A relative path can resolve differently when the launch method or directory changes.

A note application should use an explicit data directory and a versioned format. Exports can use a destination chosen by the user. Interpreting arbitrary paths from web pages, model output, or tool results turns untrusted data into file-operation authority.

Define what saving guarantees

Changing an object in memory changes process state. After a write call, data may still pass through user-space buffers, kernel buffers, and persistent devices. Closing a window and completing a save are separate events.

Writing a new file before replacing an old one can reduce partially written content. Atomic replacement does not by itself prove the new content survives power loss. Flush operations, directory metadata, and filesystem guarantees matter. Crash consistency requires platform-specific design and testing.

The interface can show unsaved changes until it receives a successful save result. On failure, preserve the draft and provide a recovery action. Background-task state also needs explicit persistence; conversation history or a global variable is not automatically a recovery record.

Observe restart behavior with a state model

Here, storage models saved contents. Each call to start creates a fresh dictionary representing process state. Editing the first draft does not change the saved version. A second startup reads the old value. Only an explicit storage update changes what the third startup reads.

storage = {"note": "saved"}
def start():
    return {"draft": storage["note"]}
first = start()
first["draft"] = "unsaved"
second = start()
assert second["draft"] == "saved"
storage["note"] = first["draft"]
third = start()
assert third["draft"] == "unsaved"
print(second["draft"], third["draft"])

The output is saved unsaved. The calls create distinct dictionaries containing immutable strings. This code starts no actual process and writes no disk file. Its simulated storage also disappears when the Python interpreter exits.

The model checks the distinction between editing and saving. It does not model locks, permissions, full disks, concurrent saves, or power loss. A real note app also needs version-conflict handling so an older draft cannot overwrite newer saved content.

Identify who owns the state before debugging

For content lost after restart, first check whether saving ran, whether it succeeded, whether it used the intended path, and whether the new process reads from that same location. Changing system permissions or deleting directories is not a substitute for this evidence.

A parent usually passes environment variables to its child. A child changing its own environment does not update the parent's environment. Standard output and standard error have distinct purposes. Working directory, exit status, and error type help reproduce failures; logs should exclude credentials and private note text.

Exercise: add failures and version conflicts

Give the simulated storage a version number. Startup reads both content and version. Saving must submit the version originally read. Reject a version mismatch while preserving the draft. Add a simulated write failure and represent unchanged, unsaved, saving, saved, and failed interface states.

Verify that failed saves preserve drafts, successful saves survive a new simulated startup, and two simultaneous drafts cannot let an old version overwrite a new one. If you implement actual file storage, use a dedicated temporary directory. Record ordinary save checks separately from crash-persistence tests.

Sources & further reading

Sources checked on 2026-11-10. Original engineering explanation; examples are teaching data, not production measurements.