Thanks for reading and thanks for all the corrections, in glorious Hacker News fashion. It's definitely a pleasant surprise to see my project here. Putting the "You" in CPU is still very much a work in progress — I was hoping to polish it a lot more and even add some more content before posting on HN later next week :)
Some backstory on me: I'm 17 and left high school a year ago to work full-time at Hack Club (https://hackclub.com/). I've been programming for as long as I can remember, and started homeschooling about 6 years ago to focus more on that (and my other interests).
Since I'm entirely self-taught, I haven't taken any college systems classes — and while I had picked up a lot, I wasn't happy with my answer to "what happens when you run a thing." So I let myself spend a shit ton of time actually learning as much as possible. What I found was that:
1. Operating systems and hardware are really fun to learn about!
2. Wow, online resources on this stuff are terrible.
A decent portion of my research ended up at PDFs of lecture slides from 2014, or StackOverflow answers that were, in fact, incorrect or vastly oversimplified.
So, I wrote Putting the "You" in CPU to hopefully provide a better resource to people who wanted to start teaching themselves all this stuff! While I don't provide perfect coverage (still need to write a couple paragraphs on SMP), it's a lot better than most of what I've seen. I also had fun drawing and making diagrams for the first time, I think I definitely got progressively better and I'm really proud of some of the drawings in the latter few chapters.
> Wow, online resources on this stuff are terrible.
I've been thinking this a lot lately, searching for things like: a detailed block diagram of the Linux kernel, how page tables work, a comparison of function call ABIs on different platforms, details about low-level network protocols, reserved Linux PID numbers, etc.
Occasionally I would find a Reddit thread with an answer, but it was mixed in with the usual Reddit dross, and I doubt that Reddit is a reliable place to store knowledge. StackOverflow was particularly unhelpful, as the questions are mostly oriented around accomplishing particular tasks, and the answers are necessarily brief.
Nowadays, for general knowledge, I skip Google's enshittified search and go straight to Wikipedia, but Wikipedia has limits on how much detail it will go into about technical topics.
I wonder if there's a need for a comprehensive wiki for all the details of computer guts? There are lots of scattered resources, but having them all compiled in one place would be awesome.
Not just operating systems, but everything that goes on under the application layer! All the way down to the bits living in the CPU registers and going out the PCIe/USB/Ethernet ports.
Another thought: a focus on staying up to date would be important. A lot of information about system internals I find is actually historical, and while that's useful sometimes, it's still a pretty big gap. Modern systems are quite poorly documented.
https://wiki.osdev.org/Expanded_Main_Page is quite good or at least a starting place. It's very x86-centric, though, and doesn't have a lot of coverage. Also, a decent portion is out of date. I used it a decent amount as at least a component of my research for You in CPU.
Agree that modern systems are even more terribly documented than systems in general. So many of the resources I found, even recent ones, were referring to stuff from the original x86 that had long changed. Almost everything I found about ELF or memory layout assumed the memory space was 4 GiB.
Finally, I hope Putting the "You" in CPU gets well-SEOed if only so people like me Googling for modern answers to these questions will find at least something. I tried to make everything as relevant to modern day tech as possible, and to link to up-to-date resources.
I had the same questions when I was about your age and found a lot of intersting answers in the Linux source code. of course it was a lot simpler back then. what really got me going was the osdev community (much smaller back then) and Intels system architecture documentation. iirc part 3a or 3b was where the good stuff is. it tells you a lot less about how things are and a lot more about how things could be. you also get to learn plenty of fun things in OSDEV too like how 32bit processors did 36bit addressing with PAE or how 64bit processors did 52bit addressing to squeeze money out of people. how computers start in 16bit mode and you have to learn systems level assembly code gymnastics to make it to long mode, why there's an 8bit mode around, and more. if you liked learning about loading binaries, you'll love how many surprises still exist. like 64bit mode still including memory segmentation but forcing it to flat mapping for lonh mode
Fantastic work! This makes for a great resource to dive into OS dev. There's a lot of detail but it's easy to digest. I suggest slightly revising the end of note #2 for chapter 2 to reflect that some async paradigms in programming languages are cooperatively multitasked. Within a process, it's generally considered safe to utilize cooperative multitasking (desirability is a different question), although I believe Erlang sticks with preemption for robustness.
> I was hoping to polish it a lot more and even add some more content before posting on HN later next week :)
That's on me, I'm a fan of your work and saw this pop up on my GitHub feed. I probably should have considered if you were ready for it to be shared. I hope the attention was mostly positive!
Hey, you’re the author of water.css! Love that project. It really helped me when I was about to launch my blog and didn’t want to get held up for months by fiddling around with CSS. Thanks a lot for providing water.css!
There are a lot of rabbit holes to go down and not enough time in my day! I cannot say in good conscience that I have gone down this one, although I've picked up a lot of factoids. I've read a couple books on the history of computing, but I'm not sure how much I actually remember — likely because I didn't go on many research tangents. Occasionally I also just get curious and Google something. So, definitely picked lots of tech-history things up, but not an expert by far. Perhaps some day I will be.
Love resources like this. I’m getting my feet wet in the RISC-V world, and it’s clear that people who want to push FOSS forward are going to need knowledge like this to get software working well on all of the SBCs that are coming out.
Just FYI, the RISC-V instruction encoding is considerably freakier than just about anything else you could be exploring except perhaps Thumb-2. They had good reasons to make it like that, but still, if you’re planning on reading hexdumps RISC-V is not going to go easy on you, even if it’s simple from most other angles.
It’s mostly the immediates, I mean, how could I not mention the immediates. The other parts are indeed arranged fairly straightforwardly, but trying to read off a 5-bit register field crossing a byte boundary when the instruction is written in little endian—or even just a three-bit field somewhere in the middle to figure out what the instruction even is—is less than pleasant.
Again, I recognize this makes things simpler, not more difficult, for the hardware, and even a compiler’s emitter will spend very little code dealing with it. But it’s not easy on the eyes.
wouldn't any programmer already know this, assuming you studied for a bachelor's degree or more?
At least if you didn't skip the computer architecture and operating systems classes.
For me it was a common thing to define an ISA, implement a simulator and assembler, and hand-write some assembly as part of learning how computers work.
A dedicated man might go the extra mile and implement it in FPGA, or write a LLVM backend.
Then there is all the operating system aspect. It's quite common to implement your own kernel there to learn, or at least to modify an existing one.
Well, the voting on this question and the other replays could explain why most code in the wild looks like it was written by people who don't know how computers work… Sad, but this here is quite telling imho.
I don't say that you need to be an expert on low level programming to write good code (sometimes this is even a hindrance as quite some of these people are prone to the premature optimization anti-pattern) but one should imho know at least the basics and have a high level overview of the low level stuff.
But frankly the reality is that even people who struggle with mere syntax, people who have no clue whatsoever what's going actually on, "contribute" code way to often to the corpus that you need to deal with in professional settings.
</rant>
Edith thinks I should at least praise the great article we're commenting on here!
Very nice write up.
The only thing that I miss a little bit is a kind of "disclaimer" that what gets presented is "just" the result of a market race, and not how computers need necessary to work like. Not going into the details of possible hardware architectures and implementation, but even on the "user facing" level (the operating system and application layer) things can look very, very differently. Just as an example: https://en.wikipedia.org/wiki/Genera_(operating_system)
Not gonna get involved in the Hacker Newsmare occurring in the comments here, but still_grokking: great point about the disclaimer and I was definitely intending to put something similar in. Article is still in WIP status!
How does Computer Science teach the basics? Doesn't most of the CS curriculum assume a Turing machine with infinite tape?
At my university, most of the classes the GP is talking about (logic design, microprocessor implementation, OS-level programming) were in the Electrical Engineering curriculum. The lowest level that the CS curriculum got to was writing a parser for a programming language.
I learned about computer architecture in my CS curriculum. It seems there’s great variance to these courses, so it’s hard to assume your experience is the same everywhere. Mine had a good deal of practical applications, prefaced by theory. I know people who went to school and didn’t learn some “basic” things, that their curriculum didn’t prioritize.
As time goes by the topics to focus also changes (should we have Machine Learning courses now?), so that’s to be expected.
This sort of gatekeeping is the kind of reason why a lot of people are put off by engineers - "oh, you don't know X? You're clearly not a software engineer".
There's a reason abstraction layers exist, perhaps you should try and figure out why your mental model of software does not account for that.
From my experience a substantial fraction of programmers learn the absolute minimum to get a job.
Then they either learn on the job or ask the same questions over and over again, never learning. The latter being the really annoying people to work with.
You can be a perfectly good e.g. web developer without knowing any of that. It would be good to know how it works but you don’t need to go that deeply into it unless you’re interested in it. And if you are, or if you really do need to know it, this is a learning resource so you can do that
Some types of low level programming require you to know about CPU architectures and op codes, but certainly not all types of programming. You can be an excellent web dev without knowing how a CPU works, for example.
How is that relevant? A computer architecture class explains the high-level concepts of an ISA and micro-architecture, covering features from across the domain.
There is never a strong focus on x86, and any kind of rote memorization has nothing to do in a higher education institution.
Are you autistic or something? Serious question. You seem to have a hard time understanding other perspectives, and you missed obvious sarcasm/hyperbole there.
Often it's the case that those who learnt to program at college/university at the age of 20 are unsettled by their peers who at 30 have two decades under their belt, having spent their fertile years from 10 or earlier programming. By 20 I'd already been writing code every week without fail for a decade. Now I'm at > 70%.
> Myself I don't understand how one could write any code without understanding how the code gets run.
I think this perhaps says more about you then it does the people to whom you're referring. If you can't understand how someone can write code to perform tasks and extrapolate from that ways that programming can be used to perform tasks, without any underlying knowledge of the architecture that it runs on, then I would suggest your education is currently incomplete.
How far down does the full stack go? Hardware? Digital electronics? Analogue electronics? Physics? Further?
Code is not just to perform tasks, at least not if we're talking about software engineering, rather than mere scripting.
Code is typically used to run a service running for a potentially long time leveraging hardware resources which are made accessible and shared with other processes through an operating system.
To ensure that the service runs well and doesn't hamper other services sharing those resources, you need at least high-level understanding of how the hardware and operating systems work.
We’re not talking about software engineering. The recommendation to read a book about basic computing is obviously not intended for computer scientists.
Depending on your definition of “service” here, I’d argue that most code is not written to run services, and certainly not long-running services. Like, core-utils is a shitton of programs, and a shitton of code, and none of that runs as a service. Most of what you mentioned in your first comment aren’t services.
Bio researchers don’t write long-running services. They don’t even really understand how computers are allocating resources. They don’t know shit about putting an HPC cluster together and managing it. Are they not writing code?
I agree with the other person who said your education seems incomplete. Yours is a very narrow view of computers and computing.
The vast majority of software written is private code that serves specific business requirements. That's not always going to be services, but it often is. Coreutils is statistically zero percent of all software.
I had a skim of the book posted here, and it looks very interesting, and it goes into a lot of detail, for example there is a page on the ELF format and how the data is packaged. Are you saying you NEED to know all this to write useful code? I sort think you don't really, not at least since high level languages came out. That said this book looks very interesting to read for curiousity.
Knowing how ELF works is certainly useful for many reasons, at least if you are building applications that compile down to ELF.
You may want to know why they are the size that they are, be able to extract meta-information, configure how the dynamic linker works, extract debug info, etc.
Some of things are actually critical for installation or deployment.
That makes sense, but if I am purely developing for the web, and maybe nodejs on the back end, for say a JIRA clone, and I am on a team, would I need to know? Probably useful. And I am for curiosity. But I don't think it is worth making developers feel bad because they haven't jumped through a particular hoop, which I think some of the sentiment on this thread seemed like it was doing.
That has not been my experience, working in the embedded space. Certain concepts are important, but you don’t have to fully understand operating systems.
It's wild that we expect doctors to know anatomy and lawyers to know legal reasoning, but software developers get some kind of magical pass on understanding how computers work. And not just a pass, a very aggressive, defensive one, too. I think it's unconscionable to call yourself a developer and not know how the computer works, because underneath all of that NodeJS magic garbage is the same operating system, processor, and logic gates.
Effective developers have, at a minimum, a basic understanding of what all of those things are doing. No, you don't need to hand-roll assembly, but maybe understand what memory is and how it's allocated, for example. Except you'd be hard-pressed to find (web) developers who can even tell you the basics of memory allocation now.
And people lament that software is slow and terrible. I wonder why.
“Anyone should already know this” is a very weird and illogic criticism towards learning material. Nobody is born knowing this, so surely such material is useful for everybody at some point.
LLVM as a project might be old, but to make its way to university's curriculum takes years or decades.
So I guess you are avoiding answering my question: can you give an example of university's curriculum, when they make students work on both FPGA and LLVM? Because that's what you were suggesting in your statement.
I didn't say there was a course that did both; in practice these things would depend on what the professor/assistants are personally researching.
The school I went to definitely did the FPGA bit as there was a research team looking into FPUs at the time. I don't think we did any LLVM, though one of the teaching assistants I was friends with was excited about it as he was specifically researching SSA transformations, but most of my exposure to that must have been outside of class.
That school specializes in theoretical computer science so I wouldn't really recommend it to anyone willing to do practical things, nor can I vouch for the quality of any remaining practical bits today.
In practice I don't really know where to get a good computer science education. It seems computer architecture has become optional in quite a few top-ranking institutions as well. And they tend to follow trends like the whole AI fad. Your best bet is to research what kind of research the associated people do.
I went to a university, not a trade school. It taught me how to learn effectively, so I can learn technical details on my own for decades outside of school.
I'm sorry that you wasted your money on an educational investment that doesn't pay dividends.
Most people have at least some gaps in their knowledge. A lot of people come to computer programming by way of other degree programs, such as EE, physics, math, even astronomy.
For anyone looking to find out more about how computers work at a very basic level, I really recommend the book "Code" by Charles Petzold. Goes from first principles all the way up.
One of the things this does well is it remarks on expectations versus what was learned in the process of compiling the information. “I expected x because y but actually it’s a because b!” Clearing up misconceptions is often one of the key steps in new knowledge formation, and it’s helpful for writers to acknowledge that people often need to overcome their misconceptions as they process the information in the writing. It’s a much more reader-friendly approach than just being like, “Well, this is correct, and if you thought something else you’re a dummy.”
Btw, Chapter 6 contains wrong explanation of fork() return values. It should be opposite than stated: in the parent, child's pid is returned and in the child it's zero; just created a pull request for that.
> For example, add eax, 512 translates to 05 00 02 00 00.
> The first byte (05) is an opcode specifically representing adding the EAX register to a 16-bit number. The remaining bytes are 512 (0x200) in little-endian byte order.
That explains only the first 3 of the 5 bytes in the instruction. What are the remaining 00s for? Did he perhaps mean that the opcode is for adding EAX to a 32-bit number?
Great article! As a self-taught web developer I find that kind of resources very valuable. It help me to move away from all the abstractions and towards a more concrete understanding of how thing work. Thank you.
This looks like a good resource, and gets into the meat very quickly, and is sort of entertaining. It is less "monad tutorial"-esque than I expected. Look forward to reading more of it.
Great resource! However, one thing left me with a question about the kernel:
From [0]:
> Since shebangs are handled by the kernel, and pull from buf instead of loading the whole file, they’re always truncated to the length of buf. Apparently, 4 years ago, someone got annoyed by the kernel truncating their >128-character paths, and their solution was to double the truncation point by doubling the buffer size! Today, on your very own Linux machine, if you have a shebang line more than 256 characters long, everything past 256 characters will be completely lost.
What is the reason for silently truncating someone's filepath? The kernel, of all places, should not be doing such things. Granted, 256-byte long file paths don't make sense, but 256-byte long path+argument strings will almost certainly happen sometimes. To just silently break someone's script is wrong.
(Author here:) I strongly agree! It should not require an understanding of the kernel's execution logic to be able to understand the limitations of shebangs. I think this is stupid design and I perhaps should've highlighted it even more as such.
A couple years ago someone attempted a patch to the kernel that would at least fail loudly in the case of oversized shebangs. This ended up causing problems for NixOS which, being Nix, already was using oversized shebangs with lots of long nix-store paths. They were previously being silently truncated but still working, and suddenly all those scripts failing caused a lapse in backwards compatibility! So the patch had to be undone and we haven't gotten anything similar since.
you shouldn't be putting complex arguments in a shebang.
it's there to tell where the interpreter for the current file is.
shebang can't even handle more than a single argument on its own. if you have `#!/bin/program -args somescript`, it will divide it into ['/bin/program','-args somescript'], which is almost never what you actually wanted. ( this is linux specific, shebangs aren't necessarily universally portable between all unices )
if you want to do something complex, make it `#!/bin/sh` and then `exec` from the current file with whatever arguments you need.
one thing that link doesn't mention is that you can use env to lookup an interpreter via the current path.
`#!/usr/bin/env python3` for example, will find whatever `python3` the users is using, without having to know where it is.
this can be useful for scripts that you will be running from virtual environments, for example, which work by overriding the PATH to the python interpreter.
you wouldn't want to do this with anything installed, however, since you wouldn't want installed programs to let the user control what interpreter to use. instead, you should just use an absolute path.
there is an option in env, -S, that can be used to parse a command line from a single string specifically to handle this limitation.
after years of avoiding much trickery in shebang lines, it seems like a questionable practice at best.
I have always thought that shebangs are absolute paths to be weird. Everything else in my Linux day to day operates by traversing the PATH, why do shebangs get to be different?
the PATH search is done in userspace by the exec'ing process. the kernel doesn't have or care what the processes' environment PATH says. it just gets the final location.
shebang processing is the kernel peeking at the first couple hundred bytes of a file to see if it should execute it using some other file instead of running it directly. there isn't a userspace yet because it's still setting the process up. even if there was a userspace, PATH searching should be done for the `exec`ing process' path, not the PATH given to the new process. so you'd have to squirrel away the calling processes path somewhere on top of everything else.
so, it would be a pain. and also incompatible with all of the previous exec system calls, since if you "fixed" it to follow the path, anything not expecting that could break.
My article is, indeed, targeted towards folks who did not do undergrad in CS! Self-taught people need better resources to understand more low level things. I have never taken a CS class, undergrad, high school, middle school, or otherwise.
Awesome work - I like the balance between the details and the high-level overview.
How do you get it all on one page? That gives you the ability to ctrl+f stuff, skip past things you know by skimming their contents, scroll back up to things you need to re-read, and way more.
This is still a WIP and I kinda wasn't expecting HN-level attention yet! I was definitely gonna make a 1-pager version, especially for offline reading (ie. public transit). Also had some people asking for PDF/EPUB.
If you press page down to scroll, at the end of the page you’ll typically scroll by a smaller amount than prior to that, which changes the level at which your eyeline should be after hitting page down to continue reading uninterrupted. In Emacs and Vim hitting page down works nicely, because they just scroll “past end”. Other programs, like web browsers, often stop at the end, causing this issue and making the reader look for the point where they stopped reading for maybe even 30-45 seconds. This seems to be a workaround for that.
The page will continue to scroll until the last line can fit to the top of the page. Nobody likes it when you can't scroll past the end, and have to then start moving your eyes/head to look at the bottom of the screen. Same with IDEs.
1. Loved discovering that shebangs are handled by the kernel in Chapter 3 - I did not know that.
2. Enjoyed reading the `execve` walkthrough.
3. I also like that you make it a point to include what the Mac OS and Windows equivalents for Linux terms are.
If you could enable opening of the graphics such as the one illustrating the hierarchical page table concept to their original high resolution, that would be nice for readability.
These projects are really fun. On the other hand, you might want to learn in a way that lets you build hardware (esp for FPGA's). For that, I suggest a few types of books with examples:
- Computer architecture or CPU design book covering modern designs with their tradeoffs
Before that, I had saved Baranov's work which showed how to turn Abstract, State Machines into actual hardware. It was like a how-to on high-level synthesis that he used commercially at Synthezza. I still share them periodically in case someone wants to build an OSS version. I also saw ASM's used in verifying compilers and hardware in separate work. There's a good chance someone can turn many things into a unified, field theory of sorts using ASM's.
Thanks for reading and thanks for all the corrections, in glorious Hacker News fashion. It's definitely a pleasant surprise to see my project here. Putting the "You" in CPU is still very much a work in progress — I was hoping to polish it a lot more and even add some more content before posting on HN later next week :)
Some backstory on me: I'm 17 and left high school a year ago to work full-time at Hack Club (https://hackclub.com/). I've been programming for as long as I can remember, and started homeschooling about 6 years ago to focus more on that (and my other interests).
Since I'm entirely self-taught, I haven't taken any college systems classes — and while I had picked up a lot, I wasn't happy with my answer to "what happens when you run a thing." So I let myself spend a shit ton of time actually learning as much as possible. What I found was that:
1. Operating systems and hardware are really fun to learn about!
2. Wow, online resources on this stuff are terrible.
A decent portion of my research ended up at PDFs of lecture slides from 2014, or StackOverflow answers that were, in fact, incorrect or vastly oversimplified.
So, I wrote Putting the "You" in CPU to hopefully provide a better resource to people who wanted to start teaching themselves all this stuff! While I don't provide perfect coverage (still need to write a couple paragraphs on SMP), it's a lot better than most of what I've seen. I also had fun drawing and making diagrams for the first time, I think I definitely got progressively better and I'm really proud of some of the drawings in the latter few chapters.
P.S. The whole thing is open source on GitHub if you want to look under the hood: https://github.com/hackclub/putting-the-you-in-cpu