flowchart TD
programs["your programs<br>VS Code, a browser, Python running your code"]
os["the operating system<br>macOS, Windows or Linux"]
disk["the disk<br>kept"]
memory["the memory<br>temporary"]
cpu["the CPU<br>the worker"]
io["the keyboard and the screen"]
programs -->|"asks for a file, for memory, for a turn"| os
os --> disk
os --> memory
os --> cpu
os --> io
classDef kept fill:#fbf1e2,stroke:#e9cfa5,color:#b26a1f
classDef temporary fill:#e8f1fb,stroke:#bcd6f0,color:#2f6db3
classDef worker fill:#eceef1,stroke:#cfd4db,color:#3a4250
class disk kept
class memory temporary
class cpu worker
6 Insides
Since this course is all about instructing machines, some idea about what a computer is will be helpful.
You are about to spend fourteen weeks giving instructions to a machine, and part of that time telling an AI to give instructions to the same machine on your behalf. It helps enormously to know, in broad strokes, what is actually sitting under your fingers. You do not need to become an electrical engineer. What you need is a mental model, a small and sturdy picture you can return to whenever the words pile up.
It’s switches all the way down
Deep down, a computer is millions of tiny switches, and each switch is either off or on. That is it. There is no secret third thing. We write “off” as 0 and “on” as 1, and one such off-or-on is called a bit, the smallest possible piece of information. One bit on its own cannot say much. But line eight of them up and you get a byte, and a byte can be in 256 different patterns, enough to stand for a number from 0 to 255, or a single letter, or one dot of color. Group bytes together and you can represent anything at all: this sentence, a photograph, a song, an entire genome, or the program that is reading them. The number 01100100 does not know whether it is the number 100, the letter 'd', or a shade of gray. Its meaning comes entirely from the piece of code that decides how to read it. When you learn later that in Python 'd' and 100 are different “types”, and a type is nothing more than an agreement about how to read some bits.
The three parts that do the work
Inside the box, the three most important components are the hard disk, the memory and the CPU. The hard disk is where your files live: your code, your data, everything. Crucially, the disk is permanent, so what you save there survives when you switch the computer off. When you save a file, you are writing it to the disk. It is roomy but slow. The memory, usually called RAM, holds whatever is running right now, meaning the program that is currently going and the values it is working on. Memory is temporary. The instant a program stops, or the computer restarts, whatever was in memory is gone. This is the single most common source of beginner heartbreak, so let me say it plainly: nothing in memory is saved unless you write it to the disk. A value your program computed but never saved vanishes when the program ends. The CPU, the central processing unit, is the worker. It does the actual computing, one tiny step at a time, but staggeringly fast, billions of steps a second. And it has one important limitation: it only ever works on what is currently in memory. So the normal rhythm of a running program goes from disk to memory to CPU. A copy of your file is loaded from the disk into memory, and the CPU works through it from there.
This gives you a habit worth forming now. Whenever something surprising happens, a value that seems to have disappeared or a change that did not stick, your first question should be whether that thing was kept on the disk or was only ever temporary in memory.
Exercise 6-1
You run a program that calculates the number of A bases in a DNA sequence and prints the answer to the screen. You then close the program. Is that count still anywhere on the computer? Where was it while the program ran, kept or temporary? Where would it need to go for you to still have it tomorrow? Before you read on, decide what you think and write it down.
A machine that was only ever imagined
The CPU does one tiny simple step at a time, just extremely fast. The idea that anything and everything can be computed as a long chain of tiny mechanical steps is older than the computer chips that drive modern computers. In 1936, a young mathematician named Alan Turing asked a beautifully simple question: what is the simplest possible machine that is able can compute anything? The solution he came up with became one of the most famous ideas in all of science, and it is small enough to fit in your head.
The Turing machine was a thought experiment, a machine Turing built on paper and in his head in order to reason about what computers could and could not do, never a device he bolted together in a workshop. People have since built working models for the fun of it, and Turing himself went on to help design very real machines, including ones that broke wartime codes (Benedict Cumberbatch in The Imitation Game). However, that his machine was imaginary is exactly what made it powerful, because Turing would then prove things about every possible computer by studying only his simple made-up one.
Here is the Turing machine: There is a long paper tape divided into squares, and each square holds a single symbol, say a 0, a 1, or a blank. There is a head parked over one square that can do only three things: read the symbol under it, write a new symbol in its place, and move one square left or right. There is a state, a little sticky note reminding the machine roughly where it is in the task. And there is a fixed table of rules which says, for every combination of current state and symbol under the head, what to write, which way to move, and which state to switch to next. That is all it does. It reads the square under the head, looks up the one rule that matches the current state and symbol, writes, moves one step, changes state, and then does it again, and again, until it reaches a rule that says stop. No screen, no keyboard, no apps. Just a head shuffling along a tape, following a table doing a long sequence of things instructed by rules.
Exercise 6-2
Grab a scrap of paper and draw three squares holding 0 1 1, put the head on the leftmost square, and set the state to A. Now follow three rules, one step at a time until a rule tells you to stop:
- In state
Areading a0: Write1, move right, stay in stateA. - In state
Areading a1: Write0, move right, switch to stateB. - In state
Breading a1: Write1, move left, and then stop.
Before you start, try to predict what the three squares will say when the machine stops, and where the head will end up. Then trace it slowly and check.
If you got 1 0 1, halted on the middle square, you traced it correctly. If not, find the step where your prediction and the rules parted ways, because that hunt is the real exercise.
You may be wondering whether all that shuffling amounted to anything, and it did, once you agree on how to read the tape. Read the three squares as a binary number, with the leftmost square holding the ones, the middle one the twos, and the right one the fours. That is backwards from the way we write numbers on paper, and it is what puts the smallest digit where the head starts. The tape you began with, 0 1 1, is then no ones, one two and one four, which is six. The tape you ended with, 1 0 1, is one one, no twos and one four, which is five. The machine subtracted one. Look at the rules again and you can watch them doing the borrowing you learned at school: walking up from the small end, every 0 becomes a 1 and the walk continues, and the first 1 becomes a 0 and the walk stops. Try the rules on 1 1 0, which is three, and you should land on 0 1 0, which is two.
What the imagined machine has to do with yours
Now the astonishing part. This ridiculous little tape shuffler can compute anything that any computer can compute, your laptop included. Turing then showed something stranger still. You can write one table of rules that lets the machine read a description of some other machine, written out as ordinary symbols on its own tape, and then act that machine out step by step. One machine that can become any machine, given the right instructions. That is the theoretical birth certificate of the thing on your desk, a general-purpose computer that runs whatever program you hand it.
So how does this paper fantasy line up with the chips in a modern computer? Closer than it looks, in three ways. First, both work through their instructions one small step at a time, and the head’s cycle of reading a square, writing a square and moving one place along is the same shape as the CPU carrying out one small operation and then the next. Second, both keep the store apart from the worker: the tape holds the symbols and the head holds the rules, just as memory holds your data and the CPU does the work on it. Third, and this is the one that matters most, the universal machine reads its instructions off the tape as ordinary symbols, which means instructions and data are made of the same stuff. That is why a program on your computer is a file like any other, sitting on the disk among your photographs, until the moment the operating system loads it into memory and lets the CPU carry it out. When Python reads your hello.py and acts it out line by line, it is doing the universal machine’s job: reading a description of a computation and performing it.
The operating system, the manager in the middle
You never actually talk to the disk, memory, and CPU directly. Sitting between your programs and the hardware is the operating system. macOS, Windows, and Linux are all operating systems. The operating system sits between your programs and the hardware, handing out memory, finding files, and sharing the CPU so your programs do not have to fight over the electronics.
As Figure 6.1 shows, when your program wants to open a file it does not go rummaging through the disk itself. It asks the operating system, and the operating system finds the file and hands it over. When a program needs free memory to work data, the operating system parcels some out. When you have a browser, an editor, and a music player all running at once, the operating system is the one sharing the single CPU between them, giving each one slices of time so quickly that they all seem to run at the same moment. It is also the operating system that listens to your keyboard and draws the windows on your screen.
Software is just files full of instructions
So what is a program, or a piece of software? It is, in the end, a file on the disk full of instructions for the CPU. A game, your browser, VS Code, Python itself, every one of them is a file of instructions that the operating system loads into memory and lets the CPU carry out. There is a catch, though. The CPU only understands one language, which is a brutally simple one. It is called machine code, and it consists of raw numbers representing the simplest possible actions (or operations). E.g., move this number here, add these two, compare them, jump to that instruction. This roughly means “put the number 4 into a register”:
10111000 00000100 00000000Machine code is made to make CPUs fast, not to be read by humans. The entire history of programming is the story of building friendlier and friendlier languages (like Python) that then translates to machine code under the hood.