Hacker News new | past | comments | ask | show | jobs | submit login
Computer Science from the Bottom Up (bottomupcs.com)
307 points by hliyan on April 29, 2021 | hide | past | favorite | 100 comments



Slightly off-topic, but... am I the only one who is troubled by the fact that the explanations of "fork and exec" are almost never, ever, discuss the rationale for picking this design instead of "CreateProcess()" a.k.a. "just_launch_this_exe_file()", or even acknowledge this alternative design? A student would probably expect that to launch an app, there is a system call that would do exactly that: you pass it the name of the application/executable file, and it launches. Instead, there is a small dance of duplicating itself and then re-writing one of the copies--try not to get tangled up in the legs while doing that!

The fork+exec has many unobvious advantages over CreateProcess, but also disadvantages, and one of them is that it's, in my opinion, a completely unexpected approach: I can't think of any other system object that can only be created by first copying another already existing object and then overwriting the newly created one if needed. Files of any kind (folder/pipe/socket/etc)? Memory mappings? Signal handling? Process groups/sessions? Users/groups? The processes seem to be unique in this regard. How did people come up with this approach? That would be an interesting discussion, I think.

Instead, it's just described as the completely self-justified, with description of "zombies" thrown in the end for boots: and the zombie processes are pretty much an accidental artifact of early UNIX design; if the fork was returning not only globally visible (and therefore unstable) PID but also an fd associated with the child process, neither wait/waitpid syscalls nor reaping duties of PID 1 would have been necessary.


From "Operating Systems: Three Easy Pieces" chapter on "Process API" (section 5.4 "Why? Motivating The API") [1]:

    ... the separation of fork() and exec() is essential in building a UNIX shell,
    because it lets the shell run code after the call to fork() but before the call
    to exec(); this code can alter the environment of the about-to-be-run program,
    and thus enables a variety of interesting features to be readily built.

    ...

    The separation of fork() and exec() allows the shell to do a whole bunch of
    useful things rather easily. For example:

      prompt> wc p3.c > newfile.txt
    
    In the example above, the output of the program wc is redirected into the output
    file newfile.txt (the greater-than sign is how said redirection is indicated).
    The way the shell accomplishes this task is quite simple: when the child is
    created, before calling exec(), the shell closes standard output and opens the
    file newfile.txt. By doing so, any output from the soon-to-be-running program wc
    are sent to the file instead of the screen.
[1] https://pages.cs.wisc.edu/~remzi/OSTEP/cpu-api.pdf


This is not enough of an explanation as these things are still possible with CreateProcess.

I do remember reading the real explanation -- it was easier to implement fork. That's it.


Running code in the target process space before the target executable is loaded is not possible with CreateProcess. Configuration of the target process has to be done by the operating system, via an ever-growing list of attributes you can pack into STARTUPINFOEX. https://docs.microsoft.com/en-us/windows/win32/api/processth...

UNIX fork/exec is also significantly faster than CreateProcess, but I'm not sure how much of that is necessary rather than contingent, or results of an ever growing list of "security" programs insisting on a veto. https://stackoverflow.com/questions/47845/why-is-creating-a-...

Fork+IPC architecture - effectively multithreading but where the processes don't automatically share memory and can crash separately - also requires fork(). However this has very much fallen out of fashion.

() I suppose you can emulate fork by calling CreateProcess with an empty executable and CREATE_SUSPENDED, then use various debug APIs to overwrite its memory map with your own, but this is very messy.


I know, but running code in the target's process space isn't necessarily required nor does the API need to take on an amazing slew of options. That the NT API did is merely indicative of its growth, not an indication that fork is the one true way.


These things are possible with APIs like CreateProcess or posix_spawn, but involve the API ending up with a million little customizable parameters in the attempt to simulate all of the behaviors that can trivially be implemented if you just support "let me run some code in the target process before I replace its memory"... which is like, exactly why "easier to implement" sort of means: something is often easier to implement if it is a single universal and general primitive that forms an elegant solution to a wide set of problems rather than being a myriad collection of hacks for various use cases. As an API--particularly on systems with copy-on-write pages--fork is amazing.


I'd like a reference for this explanation :)


Am I the only one who is troubled by the fact that the explanations of "fork and exec" are almost never, ever, discuss the rationale for picking this design instead of "CreateProcess()" a.k.a. "just_launch_this_exe_file()"

It comes from a hack in Unix for PDP-11 systems. This was before paged virtual memory. "Fork" worked by swapping the process out to disk. Then it duplicated the process table entry, with one entry set to the swapped-out copy and one set to the in-memory copy. This was simple to implement, and would still work if you invoked something big enough that both sides of the fork could not both fit in memory. Thus, a shell could invoke a big compiler. That's the real reason.

There are lots of other ways to do it. Some other systems have "run" as a primitive. Plan 9 offers all the options - share code, share file handles, share data. Windows has more of a "run" primitive. QNX has a system where a new process starts empty but attached to a shared object, with control starting in the shared object. The shared object then does the work of loading the program to be executed, so the OS doesn't have to.


> The fork+exec has many unobvious advantages over CreateProcess,

What are the advantages you see, that are not rooted in the fact that it's very difficult to create any remotely sane declarative API in a language as imperative and primitive as C? In other words which of these advantages would still apply if you wrote your OS in, say, Ocaml?

Splitting fork and exec allows you to roughly hew the process environment of a clone of the parent into shape, process state-wise, before you sacrifice it to birth the child-process which will inherit many of these desired (and generally a few undesired) traits. So one big advantage is that it allows you to re-use an existing (and growing!) set of imperative commands to modify aspects of a running process to specify the same aspects for a child process,and that piecemeal mutation is basically the only way to express anything of any complexity in C, particularly if you don't want to break API compatibility all the time.

The downside is that you generally end up inheriting a bunch of cruft that you really didn't want to, that the imperative and piecemeal nature opens up problems with race conditions, and that there is a lot of overhead only partially mitigated by various complex hacks (COW, various more "leightweight" fork alternatives, special flags to general purpose system calls that are only there to control behavior upon forking, ...).


So what would the ideal API look like, in your opinion? CreateProcess, as it exists now, has lpStartupInfo->lpAttributeList parameter which is a huge and growing list of various flags and knobs; what would an extensible, declarative API look like in an OS written in OCaml?

> you don't want to break API compatibility all the time.

Indeed you don't, unless you're okay with forcing your users to rewrite all the libraries and/or applications every couple of years. That's true of any programming environment, imperative or not.

> various complex hacks (COW, various more "leightweight" fork alternatives

Copy-on-write is as much a hack as persistent data structures are: it's just more coarse-grained.


What I hate about fork is that it fundamentally doesn't even make sense. You fork a process with a window, what happens to that window? Does it get duplicated? Do both control the same window? It has no sensible behavior in the general case without cloning the whole machine, and even then, your network isn't going to get forked. There are limited cases where it could make sense, but the fact that that's not true in general should make it fairly obvious that we need a different spawning primitive. It boggles my mind that people teach fork as if it could be the primitive.


Depends on what do you mean by window. If the window lives in a X server and you clone the client, window will obviously not get duplicated.

It's quite clearly defined what gets duplicated and what gets shared on clone()/fork(). There really is no ambiguity.


I'm not saying a particular implementation is ambiguous as to what it does. Obviously any implementation will do something. I'm saying that fork as a concept is ambiguous as to what it should do.


You're forking a process, not the whole system. Sorry, but I fail to see the ambiguity. If the window is in the process as some framebuffer, it gets cloned too. That may not mean you'll get two windows on your monitor, because your display engine is HW thing, and not a process.


It's because you're thinking "it does X! it's not ambiguous!" but still missing that the question is over why it SHOULD do X instead of Y, not over whether it does X or Y. To a user, it sure as heck doesn't make sense to fork a process and still end up with one window for both of them. For lots of processes the process is its windows as far as the user is concerned.


Users don't fork processes, it's a low-level system thing.


Why repeat the same thing as the sibling comment posted an hour ago...?


Usually because you've had the browser open for a while and are seeing the state after the parent, but before the sibling comment was posted. At least that's usually why it happens for me; refreshing to see if someone else already has said what you want to say is rarely uppermost in my mind when a reply comes to mind.

Another possibility (pretty clearly not the case here) is when there are lots of replies: either you just miss it among the others, or ypu just can't be bothered to scroll down half a mile.


Fork isn't an idea that users need to have. Devs may need to know what their OS API's fork() does. No-one else cares.


That's an argument for "I don't care about this", not an argument for "this abstraction is sensible".


POSIX defines what a POSIX compliant fork() must do.


Yes it does.


So, do threads get duplicated or shared? Or something altogether different happens to them?


That's documented in man 2 fork.


Well we do have a library call for launching a new process called posix_spawn(3), but it's complicated mainly because they are a lot of options you want to configure when launching a process: which file descriptors would you like to close, which would you like to share with the newly created process, what uid the new process should have, what working directory it should have, etc. It's just a large and complicated call.

But with fork/exec, all of that setup becomes just normal code you run after fork but before exec. You want the child not to have a certain file descriptor? Just close it after fork. You want to drop privileges when running the child? Same thing, just setuid/setgid/setgroups after fork. You want to set up resource limits for the child? Again just setrlimit after fork.

It avoids a lot of complexity in the system call itself. (Naturally, it adds some other complexity elsewhere.)


Yes, I know. But you only figure it out after you've done you fair share of IPC and management of worker processes by hand. When you're a fresh student, this "fork+exec" just seems like a pretty ridiculous way to organize things: why not just launch the executable you want to launch immediately? Nope, that's at best postponed until the chapter on IPC, at worst it's never discussed at all, so you're left puzzled and with "okay, I guess that's how things are done, if you say so..." feeling.

Oh, and by the way: we have open()/fcntl() for opening files and then fiddling with their settings instead of one open() call with 20 arguments; we could easily have had launch() with the same parameters as execve() that would launch the new process suspended, then we could use... I dunno, even fcntl() on the process's descriptor to configure all those things and then send it SIGCONT when we're done setting it up.


When you are a fresh student, and when fork/exec is too advanced for you, just use system(3). Not recommended for production use, but that's indeed the easiest way to launch an executable.

Do we optimize our system call design for fresh students or for professionals?


We were talking about the presentation of "fundamentals" in the courses on Computer Science, right? The fork/exec design is highly non-trivial and so should probably deserve more discussion, with examples of alternative designs and design considerations, than mere "that's how new processes are created, nothing special here, let's move on".


If you want to look at it as ‘fundamentals’, fork only requires the concept of a task, whereas your top-level comment's suggestion also requires (a) secondary storage, (b) organized into files, (c) that can be named.

(That's not why Unix developed with fork, though.)


The (b) and (c) are not really needed: early timesharing systems managed to launch processes straight from the punched card decks IIRC. And without (a), what do you even need the second, identical task for? I guess it could be used parallelize crunching the numbers, but that's it; and probably that should perhaps be done from the system operator layer anyway? Like, "launch 5 copies of program on this deck but mark one specific slot in memory different for them, so they would know their ID".


Besides fork() and spawn() there's another option which is to create an empty process, configure it as needed, then start it. IMO this is what simple, orthogonal primitives looks like. This generally isn't possible in Unix but AFAIK it's possible in Mach.


I think fork was a nice solution back when threads weren't common: you just fork, run as many syscalls as you want to modify the running environment, and then do exec.

Unfortunately, with threads, you're now stuck with locks that are potentially held by threads that no longer exist. So you can't call any function that may potentially acquire a lock, like printf, or your program may randomly hang. This feels... inelegant.


       After a fork(2) in a multithreaded process returns in the child,
       the child should call only async-signal-safe functions (see
       signal-safety(7)) until such time as it calls execve(2) to
       execute a new program.
And since any process can be multithreaded thanks to shared libraries being able to internally do whatever the hell they want to... yeah. Python, for example, in its implementation of subprocess module, had to shift a lot of work into pre-forked parent (such as disabling the GC and allocating all of the memory for exec()'s arguments), to add explicit "call_setsid" argument to reduce the usage of "preexec_fn", and even then, it still has lovely comments such as

            /* We'll be calling back into Python later so we need to do this.
             * This call may not be async-signal-safe but neither is calling
             * back into Python.  The user asked us to use hope as a strategy
             * to avoid deadlock... */
Well, hope is usually not a valid thread-safety strategy, but there is really not much else the Python implementation can do.


What’s this?


This seems highly focused on systems; I wouldn't call it representative of "Computer Science" as a whole. Only maybe 2-3 of the classes in my computer science degree were focused on this kind of stuff.

Still seems like a good resource, just perhaps mislabeled.


Yeah it's basically an Operating Systems book like Tanenbaum's "Modern Operating Systems".


The introduction is a much better description:

> In a nutshell, what you are reading is intended to be a shop class for computer science. Young computer science students are taught to "drive" the computer; but where do you go to learn what is under the hood?

And “Shop Class For Computer Scientists” or “Systems Engineering From The Bottom Up” would have been better titles. It’s simply misleading to suggest a course comprehensively covers “computer science” without covering algorithms or complexity, among many other things.


> This seems highly focused on systems; I wouldn't call it representative of "Computer Science" as a whole.

I'd agree with this, but it fits with how a certain set of schools teaches their core. They will teach a set of related classes around the topics here: computer architecture, operating systems, and networking, because this is often central to their research interests. Others will scatter this information across a number of unrelated courses to check the box, or skip it entirely because their research doesn't touch as heavily on these topics.

Further research on the author suggests they are from New Zealand. I'm from the UK and live in the US. What I see taught in US CS degrees is somewhat different from what I saw in the UK, and heard about from friends in continental Europe. I imagine the author's experience is similar.

A further bias from the author, and I'm making big assumptions, is that see value here because they've worked in places that do lot of system programming. Mentions of VMware and Red Hat on https://www.wienand.org.


I was a math major (in the US) so I don’t recall exactly, but I think my university offered both BA and BS degrees in computer science, where the BA focused more algorithms and complexity (overlap with math majors) while the BS focused on operating systems and hardware (overlap with electrical engineering majors). For postgraduates there is an academic MS in computer science versus a more practical MEng in software/computer engineering.

I think this is a good system - and also indicates that we are speaking somewhat past each other on what a “computer science” education looks like.


I think it's good to offer more options, but the focus on BA/BS in the US is weird. In the UK, it was common circa-2000 for:

1. BSc, BEng, MSc monikers to be meaningless across universities, but to be used consistently at the establishment.

2. Multiple degree paths to be available that went from something advertised as computer science to computer engineering.

3. A straightforward path directly into master's level computer science education.

You couldn't make an assessment based on BS vs BA because the curriculum may be similar in both cases. Oxford used BA whereas Durham used BS with both producing solid graduates that can't be differentiated in most work (further research or industry.)

My experience in the US is that hiring has kind of perverted the nature of discussions like these. The focus is on a few select algorithms and maths courses whereas the folks who've done the systems work are arguably better prepared for many real world tech roles. The last half of your degree has to be more interesting and reflective of the individual, right?


So my experience in the US for employment is that nobody notices or cares if you have a BA or BS, except possibly in unusual cases (my school actually offered a “Bachelor of Science in Theater”). So I don’t think there’s much of a “focus” in the US. There’s a loose set of conventions which aren’t nearly as important as the actual major, and are mostly reflective of internal structures and programs at the university.

MA versus MS is different (I think similar to the difference between a M.Phil versus an M.Sc). But I’ve never seen anyone dwell much on BA/BS.


Years ago, systems was the general purpose CS degree. I guess it is/was classical CS. Today, there are many specialties (data, security, algorithms, etc.), but many top-ranked CS schools still offer a traditional systems degree.


The reason for the name seems to be because of its focus on systems. I do agree that the way it's labeled isn't the best, but it's not trying to be representative of all Computer Scionce.

In fact, it seems to specifically be trying to have a different focus than a cs degree:

> This book aims to move in completely the opposite direction, working from operating systems fundamentals through to how those applications are complied and executed.


"opposite direction" tends to imply "the same content, just with a different order/framing". Whereas the OP leaves out the majority (not all, but more than half) of what people tend to put under the term "computer science" completely


Yes, the same comment appears whenever this comes up on HN. Bottom-up would actually be something more like Nand2Tetris or Petzold's code.


I’m surprised you had that many. My CS program had nothing comparable that I can remember.

Definitely not computer science.


We had one called "Systems Programming" that was a whirlwind intro to everything Unix (6 weeks on bash, 6 weeks on C, 6 weeks on Perl, a mishmash of SSH and everything-is-a-file and Unix-philosophy, etc)

We had another called "Operating Systems" that focused on things like concurrency, building a really basic memory pager, etc (mix of C and Java I think)

And we had one other called "Networking" that was sort of along the same lines as "Operating Systems" but focused on, well, networking. Mostly Java; we wrote our own application-layer protocols, that sort of thing

But yeah pretty much everything else - including the more "concrete" programming courses like Data Structures and Algorithms - was fairly abstracted away from any specific system


Nice, and now that I think of it we did have an operating systems class. I know better than to trust 25-year-old memories.


My undergrad CS degree I don't think contained any sort of OS course, other than maybe a survey of them but there was a graduate level class that was required for any higher degree. I took it before I graduated with an undergrad because it was interesting, and it was quite extensive as I recall. That was well over 20 years ago, however.


That sounds a lot like part of the UofT CS program.


As much as I appreciate such material, but this is not "computer science". Maybe "computer engineering", or some mix of "software engineering", "systems engineering", those things.

Computer science is about algorithms, complexity, possibly foundations of programming languages, cryptography, database theory, those things. Not about Unix or file formats.

Those are important too, but not "computer science".


I think these CS books should start with a philosophical look at what kind of thing computation actually is, rather than diving straight into UNIX file system structures with barely any context.

But I guess that would be computer science from the top down.


Yeah, the first page of this book being "Everything is a File in Unix" is already so high-level and far above what I would call the "bottom-up" of computer science that this book should be renamed "Unix from the bottom-up."

The "bottom" of computer science would be mathematically explaining what it means to compute, and building up to the abstraction of a Turing machine, and eventually getting to a concrete implementation of what we call programming and code. This book has code on page 2 - but code is nowhere near the bottom.


Do you know a book like this? Sounds interesting to me as someone who is very interested in computing, but never went to university.

Specifically "mathematically explaining what it means to compute, and building up to the abstraction of a Turing machine"

Thanks


Take a look at: Understanding Computation: From Simple Machines to Impossible Programs by Tom Stuart.

It's pretty much "computation theory for the everyday programmer". It uses Ruby to implement things like Turing Machines and the lambda calculus. Extremely accessible, and a lot of fun.


Thank you


I am a theorist and I would say, that the bottom - as it is the foundation - is mathematical logic. And you start from there.

Baffingly enough: All this theory is easily understood with 7th-grade math.

Approaching from some specific piece of engineering is surely top-down.


Good book on that is Introduction to the Theory of Computation by Michael Sipser.

One could argue it's from the very bottom up, not much practicum lies in that text.


This book was one of the best parts of my CS degree. It's the only textbook which I read cover-to-cover because the writing and narrative were so cohesive. Highly recommend.


Richard Feynman's lecture is a great resource for this: https://www.youtube.com/watch?v=EKWGGDXe5MA

He proposes a file clerk that gets progressively dumber and faster until they get so dumb that they can be simulated by an electronic circuit.


Start with a single instruction, like SBNZ (substract and branch if not equal to zero), and build a modern operating system with it.


If you truly want to learn computer science from the basics, I would recommend starting with Nand to Tetris https://www.nand2tetris.org/ and the corresponding book and labs (and Coursera courses), along with Code the hidden language... They cover how a computer is actually build from first principles. After this, Structure and Interoperation of Computer Programs and the Algorithm Design Manual.


Past discussions with comments (including the author addressing the naming issue):

https://news.ycombinator.com/item?id=13249675 (2016, 157 comments)

https://news.ycombinator.com/item?id=21903007 (2019, 77 comments)


Also

Computer Science from the Bottom Up - https://news.ycombinator.com/item?id=7611093 - April 2014 (44 comments)


This is not computer science. I see nary a reference to data structures or algorithms, or math of any kind.

These are all engineering concerns, not science.


Computer science isn't science.


A program often is a model.


hmm, applied science is not engineering ?


Applied science is engineering, by and large, but it's worthwhile to have different words for different things. 'Engineering' is one, 'Science' is another, even though the latter is useful to the former.

Of course, Computer Science is almost no science, most degrees are some blend of math and engineering.


As with anything that entirely depends who is at work. Watching Linus Torvalds is going to make you arrive at your answer, but if you followed Don Knuth, you may have to reconsider.


> All modern architectures would be considered RISC architectures

no no no no no no no no

It is sort of like RISC on the inside (uOps), but x86 is __not__ RISC on the outside, which means it has to have a thumping great decoder and a bunch of microcode using power all the time.

Locally even if the microarchitecture could be considered a RISC, the architecture of an X86 computer cannot be considered RISC both on account of the irregularity of the instruction set and the limits to instruction-decode throughput because reasons already mentioned.


I think modern architectures are neither RISC nor CISC.

Successful modern architectures have adopted features of both. Even the most RISC architecture these days likely has AES instructions. Even the most CISC architecture is using RISC like micro ops.


> Even the most RISC architecture these days likely has AES instructions

AFAIK these sorts of instructions are typically things that would be trivial to implement in hardware but extremely cumbersome to implement in software, like shuffling a bunch of bits around (ARM's "JavaScript instruction" is another famous example). These sorts of things would only require a single micro-operation (or a very small number of uops).

My understanding is that the big distinction between CISC/RISC comes from things like addressing modes, by which a CISC processor lets you cram many hardware operations into a single software instruction. For instance, on x86 you can write an instruction like 'ADD rax, rbx, [rcx + 0x1234 + 8*rdx]' that performs several additions, a bit shift, and a memory access. Whereas on ARM, you would have to split those multiple hardware operations into multiple instructions.

> Even the most CISC architecture is using RISC like micro ops

Yes, but as the parent comment points out, that requires a complicated decoder with limited throughput. Using a RISC architecture throughout therefore moves some (but not all) of that instruction-decoding work into the compiler.

(I'm not at all knowledgeable in the field of processor design, so I would be happy to be proven wrong.)


I mean, they do provide a footnote explaining further:

> Even the most common architecture, the Intel Pentium, whilst having an instruction set that is categorised as CISC, internally breaks down instructions to RISC style sub-instructions inside the chip before executing.


That's what I'm saying is wrong. Note that the instructions are RISC style i.e. some fairly large instructions decode into a pretty small number of micro-operations, which implies said "RISC" operations are actually quite bulky.

Issue-width isn't everything, but ARM chips are really showing the limitations of x86.


Addendum: "Wrong" is also wrong. More nuanced would be to say that for an introduction to architecture (i.e. for an engineer to draw a trend line with a fat pen on a graph), this is the kind of detail that actually matters when it comes to intuition-building, so it's misleading.


I'm disappointed this didn't start from "sand", quantum mechanics, silicon doping, electromagnetism, electronics, transistors, gates and latches, etc.

Also, Computer Science is a much broader field than what this website addresses including very little science but actually a mixture of math (e.g. P vs NP stuff) and engineering (both hardware and software), this website addressing a subset of the engineering part.


is there some real "bottom up" curriculum? in retrospect I feel lucky to have grown with the tech:

* TI58C programmable calculator, a handfull of registers, load store, conditional jump, that's it, essentially a very simple assembly-like language. * then CBM basic, two letter variable names, only globals, line numbers and goto/gosub * then (turbo) pascal with scopes, still single process unicore everything * then a little 6502 assembly (not my cup of tea) * then OS/2 and unix, and Linux when it started, multiprocessing (and multi user) on unicore * ...and proper computer science (as in von Neuman, Turing, Chomski hierarchy, compilers, sorting searching and complexity, graphs, etc) * then chroot * then virtualization (on the mainframe lol, but with Linux) * then containers and in parallel to the virtualization axis threads and green threads

I'm grateful this allowed me to grow into the concepts step by step and grow an intuition for what's happening.

and I wonder if there is some kind of "bottom up" curriculum somewhere that follows such a path?


Computer Engineering departments and degrees emphasize the stuff you mention more than CS does.

CE largely extends its focus on software up to networking and O/Ses and down to compilers and instruction sets. And on the hardware side, from motherboards, interconnects, and peripherals down to CPUs, with side trips into non-PC architectures like SoCs (e.g. Raspberry Pi) and GPUs.

The CE emphasis seems to be on the engineering tradeoffs of computer hardware design and implementation (at the symbolic level) and how it interacts with software. However, CE doesn't seem to take the next step down into VLSI or power or heat management or chip failure or transistor microelectronics, largely which remain the purview of EEs.

Not all CE programs are strict about these emphases, however. Some adopt the name CE without moving appreciably away from mainstream CS. I suspect the name 'CE' better serves some professors' priorities and helps them sell their work as being rooted in engineering rather than in the abstractions and symbols rooted in math, from whence CS came.


I can't be the only one, but I feel pretty sure I'm in a minority among computer programmers. This "bottom-up" approach is just not the way my brain works. I definitely feel I'm a top-down sort of thinker and learner, and this puts me at a disadvantage, regarding the literature and approach in this profession.

Are the terms describing the two approaches here "empirical" versus "rational"? I'm talking about building bit by bit as opposed to getting an overview and then drilling down. I definitely feel like I benefit from the second. And I almost feel like it may not be too strong to suggest that I'm actively harmed by the first.

Something like the author's approach is great, for my purposes, when I already pretty much understand something. But I can't start here.

Again, I feel like I'm in a minority. But anyone else have this turn of mind, too? How do you cope? Do you just look for other materials?


You put into words what I've been feeling for a long time. So I guess you're not alone. I 'cope' by just turning it around: finding some meaningful project, decide what I want it to be, and then find out one step at a time how to get closer. That starts with me finding out - and then being comfortable enough with - a big overview of the project. Somehow I keep coming back at the 'work is like a hill' metaphor from Ryan Singer in his book Shape Up (from basecamp). https://basecamp.com/shapeup/3.4-chapter-13#work-is-like-a-h...


I think it would be good to re-package this information, but for me, the critical thing that stuff like this accomplishes is comprehensiveness. You have to go through so many classes at school just to have all the knowledge base covered at all. Everything is compartmentalized by default, so that's kind of the mother of all bottom-up approaches.

Top-down takes time and reflection, and re-packaging.



Just read page one and two and I already LOVE the conversational style of writing from the author. Who the hell decided we should write textbooks in such a dry and formal way (this has been my experience). Maybe the textbooks fail me because the author is not able to translate their excitement into text (I have ADD, so there's that too)? Of course it's also due the nature of the copyright/IP system, together with the hierarchies inherent to capitalism. The competition between workers selling their labor works to the advantage of employers/capitalists.

Anyway, learning is basically just a long conversation where we learn about the feedback loops about whatever concept or system we are trying to understand and interact with. For me this author's style (and others like him) works a million times better to do that in an intuitive way.


Looks more like computer engineering than computer science.


Yeah, that kinda bothered me too.

>Computer Science is no more about computers than astronomy is about telescopes.


In the beginning, bell labs invented the transistor...


I mean there were vacuum tubes and relay machines before that :)

IMO relays are a bit easier to understand than transistors because it's a bit easier to understand how to implement various logic gates with them with only very basic knowledge of electronics.

Also, very satisfying clicking sounds: https://www.youtube.com/watch?v=JZyFSrNyhy8&t


This is a free ebook, so i'm not complaining, but this part did make me laugh

Atomic Operations

Explain what it is.

and in case anyone is curious: https://wiki.osdev.org/Atomic_operation


https://csunplugged.org/en/

“Computer science” is a terrible name. Astronomy is not called “telescope science”, and biology is not called “microscope science”.


Telescopes and microscopes are subjects of theory of optics, which studies the nature of light.

The "computer" in "computer science" is referring to turing machines, automata, etc, which are subjects of theory of computation, which studies the nature of computable functions.


Does anyone have a comparison between this and Computer Systems: A Programmer's Perspective? I was planning to read that book and this looks like a similar resource.


CSAPP is like the theory behind low-level (C/C++/etc) programming. This is more like a sketch of the concepts behind operating systems. CSAPP is also much longer and more technical. Where they overlap CSAPP is going to be much more complete.


It's a good book of introduction of CS. I hope I could read this book when I was a student at university.


a real bottom up would start from transistors, universal xor, flip flop memory, code execution, algorithmic complexity, data structures, abstraction, recursion, graphs, concurrency, distributed system design.


> transistors, universal xor, flip flop memory,

These are the 'bottom up' of computer engineering, which are implementation details (my course included semiconductor physics as well). Computer science tends to look at theoretical constructs like Turing machines instead of these. The remaining topics which are indeed computer science.



The ELF binary format, really? This is like a course advertising itself as an introduction to normative ethics and proceeding to lecture people on canon law in the 14th century in Flanders. Like sure if you into obscure shit this might be interesting but dunno what one has to do with the other.


Well, more like the common law in the 21st century in the USA. Quite useful in nowadays practice, but obviously neither universal nor eternal. But could be a good starting point as a running example, or a reference point when comparing to the "civil law" systems.


Not sure what's obscure about ELF. Almost every one of the programs I use is wrapped in ELF format, except for maybe a bootloader and scripts.

If you want to learn from bottom up, then learning what programs are seems like an obvious approach.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: