When you count, it’s 1, 2, 3, …, 9, 10, etc. What happened at 9? You ran out of unique numbers, so you carry over one place and keep counting, now instead using two digits: …, 10, 11, 12, etc.
What does a two-digit number like 12 mean? It means “1 ten and 2 one’s.” You can break any number down this way, 3 thousands, 5 hundreds, no tens, and 7 ones is 3507. Since each place signifies a power of ten, we call this the base-10 numbering system, it’s “based on 10.”
Now imagine you have a bunch of wires lined up, let’s say four of them. The left most wire represents thousands, the next one hundreds, then tens, and ones. Each one can have a certain amount of current flowing through it: no power, 1 amp, 2 amps, etc, up to 9 amps. So if you want to represent the number 3507, the left-most wire has 3A, the next one 5A, then no power, then 7A.
This collection of wires is called a “register” in a CPU. The reason your computer needs to be plugged in all the time is that if it’s holding this number in a register and you shut off the power, then all those wires go to no current. When you plug it back in, there’s nothing there and the information has been lost. It takes constant power to represent a number.
So what? Why do this? Well, let’s say you have two registers. That means you can represent two different four-digit numbers. Now let’s add a third register, but this one is special, the number in this register corresponds to an instruction like “ADD” or “SUBTRACT”, and this chip is designed so that when there are two numbers in the first and second registers, if the third one has the instruction for “ADD”, the CPU automatically figures out how to adjust the value in the first register to become the sum of the two numbers (and maybe it clears the second register, maybe it doesn’t … depends on how you design your chip).
Now you have a very basic idea of how a CPU works. But how do you get data in and out of these registers? Well, there’s instructions in that special third register that mean “load a number from memory into register 1” and “write the number in register 1 to memory.” Memory is RAM, random access memory, and it’s just a big bank of registers that does nothing but store values the CPU might want to read and write. How do these instructions know where to look in memory? Good question! They use register 2. The value in register 2 tells the instruction the memory location to read or write.
A simple program might be (R1 is register 1, R2 is register 2):
PUT 1, R2; // puts the value 1 in R2
LOAD1; // loads the value from memory location R2 into R1
PUT 5, R2; // puts the value 5 in R2
LOAD2; // loads the value from memory location in R2 into R1
ADD; // adds values in R1 and R2, replacing value in R1 with sum
PUT 2, R2; // puts the value 2 in R2
WRITE1; // writes the value in R1 to memory at location in R2
This program loads the value from memory location 1 into register 1, the value at memory location 5 into R2, adds those values, then writes the result to memory location 2. (All of the choices of memory locations are arbitrary, they don’t mean anything, just to demonstrate how to read and write values at different memory locations.)
This is all very simple, right? Notice that this only allows a computer to execute one program at a time. Your computer does lots of things at a time, though, right? How?
A single CPU actually *can* only do one thing at a time. It makes it seem like it’s doing multiple things at once by a technique called “multitasking,” and it works like this…
Each program, like the one above, thinks it has the entire computer to itself. In fact, the operating system is the granddaddy program that is only making it seem this way. When the user requests the OS to start up a program, the OS declares a chunk of memory to belong to that program and, as far as the program knows, that’s all of the memory. Then, the OS runs that program for a bit, then suspends it, writes out all of the values in the CPU to its own memory block (allocated to the OS itself), then it swaps in the values for some other program, runs that one for a little bit, etc. It just keeps running all the different programs a bit at a time, and it happens so fast that it seems to you as if all the programs are running at once.
Now finally to get to the question you asked. When a computer starts up, the first thing it does is load the operating system, which is basically just a program itself. The job of the OS is to run other programs. When it starts up, one bunch of programs it always runs are called “device drivers.” A device driver is a little program that controls a single device. For instance, your keyboard has a device driver, and when you install the operating system the first time, it figures out which keyboard you have and it adds that keyboard’s device driver to the list of programs it will start automatically once it’s done starting up.
Once the device driver for your keyboard starts up, what it does is look at a small bit of memory that holds keystrokes and, if it sees that there’s nothing there, it goes to sleep. When the user hits a key on the keyboard, like ‘L’, an electrical connection is physically made under the key which triggers a chip on the motherboard (not the CPU, but a chip in the chipset) to read the value of that key (‘L’) and write it to the memory belonging to the device driver.
The CPU goes on its merry way, eventually getting around to swapping in the device driver program, which sees that there’s something to read in its little memory buffer. How exciting! So at that point it sends a signal to the OS called an “interrupt”, which tells it to deliver this bit of data to whatever program is currently the one “with focus” (the one that the user would expect input to go to). So then the OS swaps out the device driver, swaps in the program with focus, and says, here you go, here’s an ‘L’.
Now at some point, if this program is expecting input, then it already registered a listener that is waiting for user input, and when that event comes in, it triggers that event listener, which does whatever it’s supposed to do (which in this case is probably stick that ‘L’ into some bit of memory and display it on the screen, probably where the cursor currently is). If the program didn’t expect any user input, then there is no listener and the input just gets discarded with no effect.
Interrupts are how at the lowest level computers deal with events happening. Like if your network card suddenly starts receiving data, it might have a small amount of memory to buffer that data, but it can’t store like an entire YouTube video or whatever. So it sends an interrupt to the CPU telling it “you better stop what you’re doing right now and deal with this,” and the CPU does.
In the old days, with early computers, it was common to see your computer freeze up when something happened, and then all of a sudden it would tell you “download complete” and things would go back to normal. Nowadays, almost all of this kind of processing is done using chips in the chipset so the CPU can keep on going uninterrupted. Also, each “CPU” today is actually many CPUs packed into one (this is what “cores” are, just multiple CPUs stuck in the same chip).
Finally, all throughout this post I took some liberties with the truth. Registers don’t actually store 0 through 9 on a single wire, that actually turns out to be an electrical engineering nightmare. Instead, computers use base-2 number system so that each wire in a register is either on (representing a 1) or off (representing a 0), and numbers in binary are:
0 = 000
1 = 001
2 = 010
3 = 011
4 = 100
5 = 101
etc.
The number of wires in a register these days is 64, meaning that each register can store a binary value with up to 64 digits. This is what it means when someone says your computer is 64-bit.
It might seem impossible that a computer can actually work this way, like the sample program I put up at the top. It seems like there would have to be billions and billions of these instructions running across thousands of different programs on a modern computer—and that’s exactly what’s happening. The way it all works is by building up layer upon layer of abstraction, so a computer programmer (such as myself) doesn’t actually write programs like the one above. We use programming languages that are much more expressive, and we use a huge technology stack consisting of interpreters, compilers, linkers, etc, to do all the work of turning the program we write into the program the computer can actually run. So a few lines of code in a modern program can translate into a whole lot of complicated instructions at the level of the CPU.
Latest Answers