Course outline · 0% complete

0/28 lessons0%

Course overview →

The full trace: python3 hello.py

lesson 9-1 · ~12 min · 27/28

One command, every concept

Typing python3 hello.py and pressing Enter sets off the whole story below, and every step has already been covered.

  1. The shell reads your keystrokes. It is just a user-space process, blocked on a read syscall from the keyboard, from lessons 6-2 and 8-1.
  2. The shell asks the kernel for a child process and has it load the python3 program from disk into RAM, from lessons 3-3 and 1-1. The kernel builds a process with a fresh PID, a brand-new page table mapping its private virtual address space, an empty stack and heap, and an entry in the process table, from lessons 3-1, 3-2, 4-1, and 4-4.
  3. The scheduler starts giving it time slices, from lesson 6-1, and the CPU begins the fetch-decode-execute cycle on Python's instructions, from lesson 1-2.
  4. Python reads hello.py with open and read syscalls, decoding UTF-8 into text, from lessons 7-1 and 2-3, and starts interpreting it.

Notice how much has happened before a single line of hello.py runs. Steps 1 through 3 are pure operating system work, which is why launching a process costs milliseconds while calling a function costs nanoseconds.

The ending of the same command

  1. hello.py calls print("Hello!"). The text goes to a buffer, the newline flushes it, the write syscall crosses into the kernel, the bytes land in file descriptor 1, and the terminal draws glyphs, from lessons 7-2 and 8-2.
  2. The script ends. Python exits with code 0. Had it hung instead, Ctrl-C would have delivered SIGINT, from lesson 3-4. The kernel frees the process's memory and page table, closes its descriptors, removes the process-table entry, and hands the exit code to its parent, the shell, from lessons 3-3, 3-4, 4-3, and 4-4.
  3. The shell wakes up. It had been blocked waiting for its child, so it sees exit code 0 and prints the next prompt.
PhaseWho is doing the work
steps 1 to 3the shell and the kernel
step 4 to 5Python, with kernel help at each syscall
step 6 to 7the kernel, then the shell

Elapsed time is maybe 30 milliseconds, containing thousands of context switches system-wide and every single mechanism in this course.

1. shellyou press Enter2. kernelnew process, loads python33. schedulergives time slices4. python runsreads hello.py5. print("Hello!")write syscall, terminal draws6. exit(0)process cleaned up7. shell wakes with exit code 0prints the next prompt
The life of one command, through every layer this course covered.

The order of the four milestones

For python3 hello.py, the four events happen in this order:

OrderEvent
1the kernel creates a new process and loads python3
2the scheduler gives the new process its first time slice
3a write syscall sends Hello! to the terminal
4the shell receives exit code 0

The reasoning is a chain of prerequisites. The process must exist before it can be scheduled, it must get CPU time before its code can run far enough to print, and it must die before the parent can collect an exit code.

The dependency in each step is worth stating, because it is what makes the order forced rather than merely typical. No amount of scheduling luck can move an exit code before the process that produced it has finished.

The state of a process waiting on the disk

While python3 waits for hello.py's bytes to arrive from the disk, its state is waiting, also called blocked, and the kernel is what moved it off the ready queue.

A blocking read syscall parks the process in the waiting state from lesson 3-1, the scheduler spends its slices on other processes as described in lesson 6-2, and the kernel wakes it when the disk data arrives.

UnitContribution to this one moment
3the waiting state exists
6the scheduler skips waiters
8the syscall is what blocked it

Three units are cooperating in a single instant, and the process itself experiences none of it. From inside, read simply took a while to return.

Naming the number handed to the shell

The number 0 that the kernel hands from the dying python3 process to the waiting shell is the exit code, also called the exit status.

Every dying process reports one, with 0 for success and non-zero for failure, and the parent process receives it. This is the value from lesson 3-3.

ConsumerHow it uses the code
the shellexposes it as $?
&& chainsruns the next command only on 0
CI systemspass or fail the build

Zero means success by universal convention, which is what allows tools written decades apart in different languages to be chained together without agreeing on anything else.