One of the reasons people have become so enamored with computers is that they enable you to experience the new worlds you can create, and to learn what’s possible. In mathematics you can engage in mental gymnastics about what might be. For example, when most people think of geometry, they think of Euclidean geometry. But the computer has helped people visualize different geometries, ones that are not at all Euclidean. With computers, you can take these made-up worlds and actually see what they look like. Remember the Mandelbrot set—the fractal images based on Benoit Mandelbrot’s equations? These were visual representations of a purely mathematical world that could never have been visualized before computers. Mandelbrot just made up these arbitrary rules about this world that doesn’t exist, and that has no relevance to reality, but it turned out they created fascinating patterns. With computers and programming you can build new worlds and sometimes the patterns are truly beautiful.
Most of the time you’re not doing that. You’re simply writing a program to do a certain task. In that case, you’re not creating a new world but you are solving a problem within the world of the computer. The problem gets solved by thinking about it. And only a certain kind of person is able to sit and stare at a screen and just think things through. Only a dweeby, geeky person like me.
The operating system is the basis for everything else that will happen in the machine. And creating one is the ultimate challenge. When you create an operating system, you’re creating the world in which all the programs running the computer live—basically, you’re making up the rules of what’s acceptable and can be done and what can’t be done. Every program does that, but the operating system is the most basic. It’s like creating the constitution of the land that you’re creating, and all the other programs running on the computer are just common laws.
Sometimes the laws don’t make sense. But sense is what you strive for. You want to be able to look at the solution and realize that you came to the right answer in the right way.
Remember the person in school who always got the right answer? That person did it much more quickly than everybody else, and did it because he or she didn’t try to. That person didn’t learn how the problem was
supposed
to be done but, instead, just thought about the problem the right way. And once you heard the answer, it made perfect sense.
The same is true in computers. You can do something the brute force way, the stupid, grind-the-problem-down-until-it’s-not-a-problem-anymore way, or you can find the right approach and suddenly the problem just goes away. You look at the problem another way, and you have this epiphany: It was only a problem because you were looking at it the wrong way.
Probably the greatest example of this is not from computing but from mathematics. The story goes that the great German mathematician Carl Friedrich Gauss was in school and his teacher was bored, so to keep the students preoccupied he instructed them to add up all the numbers between 1 and 100. The teacher expected the young people to take all day doing that. But the budding mathematician came back five minutes later with the correct answer: 5,050. The solution is not to actually add up all the numbers, because that would be frustrating and stupid. What he discovered was that by adding 1 and 100 you get 101. Then by adding 2 and 99 you get 101. Then 3 and 98 is 101. So 50 and 51 is 101. In a matter of seconds he noticed that it’s 50 pairs of 101, so the answer is 5,050.
Maybe the story is apocryphal, but the point is clear: A great mathematician doesn’t solve a problem the long and boring way because he sees what the real pattern is behind the question, and applies that pattern to find the answer in a much better way. The same is definitely true in computer science, too. Sure, you can just write a program that calculates the sum. On today’s computers that would be a snap. But a great programmer would know what the answer is simply by being clever. He would know to write a beautiful program that attacks the problem in a new way that, in the end, is the right way.
It’s still hard to explain what can be so fascinating about beating your head against the wall for three days, not knowing how to solve something the better way, the beautiful way. But once you find that way, it’s the greatest feeling in the world.
VI
My terminal emulator grew legs. I was using it regularly to log onto the university computer and read email or participate in the discussions of the Minix newsgroup. The trouble is, I wanted to download things and upload things. That meant I needed to be able to save things to disk. In order to do that, my terminal emulator would require a disk driver. It also needed to get a file system driver, so that it would be able to look at the organization of the disk and save the stuff I was downloading as files.
That was the point where I almost gave up, thinking it would be too much work and not worth it. But there wasn’t much else to do. I was going to classes that spring, and they weren’t especially challenging. My sole outside activity was the weekly meeting (party) of Spektrum each Wednesday night. Social non-animal that I was, that became my only occasion to do anything other than program or study. Without those meetings (parties), I would have been a total recluse that spring, instead of a near-total recluse. Spektrum provided a built-in framework for a social life of some sort, and I don’t think I over missed one of their events. They were important to me—so important, in fact, that I sometimes lost sleep anticipating those meetings, hoping not to feel self-conscious about my lack of social graces or my nose or my obvious absence of a girlfriend. This is standard geek stuff.
What I’m trying to say is that I didn’t have a heck of a lot of other interesting things going on. And the disk driver/file system driver project would be interesting. So I said, I’ll do this. I wrote a disk driver. And because I wanted to save files to my Minix file system—and because the Minix file system was well-documented anyway—I made my file system compatible with the Minix file system. That way, I could read files I created under Minix and write them to the same disk so that Minix would be able to read the files I created from my terminal emulation thing.
This took a lot of work—a program-sleep-program-sleep-program-eat (pretzels)-program-sleep-program-shower (briefly)-program schedule. By the time I did this it was clear the project was on its way to becoming an operating system. So I shifted my thinking of it as a terminal emulator to thinking of it as an operating system. I think the transition occurred in the hypnosis of one of those marathon programming sessions. Day or night? I can’t recall. One moment I’m in my threadbare robe hacking away on a terminal emulator with extra functions. The next moment I realize it’s accumulating so many functions that it has metamorphosed into a new operating system in the works.
I called it my “gnu-emacs of terminal emulation programs.” Gnu-emacs started out as an editor, but the people who created it built in a host of functions. They intended it to be an editor that can be programmed, but then the programmability part took over and it became the editor from hell. It contains everything but the kitchen sink, which is why sometimes the icon for the editor is actually a kitchen sink. It’s known for being a huge piece of programming effort that has more functions than any editor needs. The same thing was happening with my terminal emulator. It was growing to be much more.
To: Newsgroup: comp.os.minix
Subject: Gcc-1.40 and a posix question
Date: 3 Jul 91 10:00:50 GMT
Hello Netlanders,
Due to a project I’m working on (in minix), I’m interested in the posix standard definition. Could somebody please point me to a (preferably) machine-readable format of the latest posix rules? Ftp-sites would be nice.
Okay, this is the earliest public evidence that a geek in Finland was willing to test the bounds of his computing skill. The POSIX standards are the lengthy rules for each of the hundreds of system calls in Unix—what you need in order to get the computer to perform its operations, starting with Read, Write, Open, Close. POSIX is a Unix-standards body, an organization comprised of representatives from companies that want to agree on common guidelines. Standards are important in order for programmers to be able to write applications to the operating system and have them run on more than one version. The system calls—particularly the important ones—would give me a list of the various functions needed for an operating system. I would then write the code to make each of those functions happen in my own way. By writing to the POSIX standards, my code would be usable by others.
I didn’t know at the time that I could have bought those rules in hard-copy form directly from POSIX, but it wouldn’t have mattered anyway. Even if I could have afforded the cost, it always took a long time to get things shipped to Finland. Hence my appeal for a version that I could download for free from an ftp site.
Nobody responded with a source for the POSIX standards, so I went to Plan B. I tracked down manuals for the Sun Microsystems version of Unix at the university, which was operating a Sun server. The manuals contained a basic version of the system calls that was good enough to help me get by. It was possible to look at the manual pages to see what the system call was supposed to do, and then set about the task of implementing it from there. The manual pages didn’t say how to do it, they just said what the end results were. I also gleaned some of the system calls from Andrew Tanenbaum’s book and a few others. Eventually somebody sent me the thick volumes containing the POSIX standards.
But my email message did not go unnoticed. Any knowledgeable person (and only knowledgeable people would be reading the Minix site) could tell that my project would have to be an operating system. Why else would I want the POSIX rules? The message aroused the curiosity of Ari Lemke, a teaching assistant at Helsinki University of Technology (where I would have studied had I not been so interested in studying theory). Ari sent me a nice reply, offering to make a subdirectory on the university’s ftp site available for when I would be ready to post my operating system for anyone who might be interested in downloading it.
VII
Ari Lemke must have been quite an optimist. He created the subdirectory (
ftp.funet.fi
) long before I had something I wanted to release. I had the password, and everything was set up for me to just log in and upload stuff to it. But it took about four months for me to feel I had anything I was willing to share with the world, or at least with Ari and the few other operating system freaks with whom I had been exchanging email.
My original goal was to create an operating system that I could eventually use as a replacement for Minix. It didn’t have to do more than Minix, but it had to do the things in Minix that I cared about, and some other things I cared about, too. For example, not only was the Minix terminal emulation bad, but there was no way of performing the job-control function—putting a program in the background while you’re not using it. And memory management was done very simplistically, as it still is in the Mac OS, incidentally.
The way you create an operating system is to find out what the system calls are supposed to do, and then write your own program to implement those system calls in your own way. Generally speaking, there are a couple of hundred system calls. Some of them can represent multiple functions. Others are quite simple. Some of the more fundamental system calls are really complicated and depend on a great deal of infrastructure being there. Take the system calls of “Write” and “Read.” You need to create a disk driver in order to write something to disk or read something from disk. Take “Open.” You have to create the entire file system layer that parses the names and figures out where on the disk everything is. It took months just to write the “Open” system call. But once it was in place, the same code could be used for other functions.
That’s how the early development was done. I was reading the standards from either the Sun OS manual or various books, just picking off system calls one by one and trying to make something that worked. It was really frustrating.
The reason: Because nothing is happening, you can’t really see any progress. You can make small test programs that test whatever it is you just added. But that doesn’t really accomplish anything. After awhile you get to the point where, instead of just reading through a list of system calls, you give up on that approach. It’s getting complete enough that you want to run a real program. The first program you have to run is a shell because, without a shell, it’s pretty hard to run anything else. And besides, the shell itself contains many of the system calls you will need. Get it running and you will be able to print out a running list of the system calls you haven’t implemented.
In Unix, the shell is kind of the mother of all programs. It’s there to start up other binaries. (A binary is a program in the 1’s and 0’s that a machine reads. Whenever you write a program in a computer language, you then compile the source code and it becomes a binary.) The shell allows you to log on in the first place. Okay, traditionally in a real Unix system the first program you run is called init, but init really needs a lot of infrastructure in order to work. It’s kind of a controller for what goes on. But when you don’t really have anything that works, there isn’t any point to having init.
So instead of starting init, the first thing my kernel did was to start the shell. I had implemented about twenty-five system calls and, as I mentioned, this was the first real program I was trying to run. The shell wasn’t something I had written myself. I had downloaded onto a disk a clone of the Bourne Shell, which was one of the original Unix shells. It was available over the Internet as free software, and its name was derived from a bad pun. The guy who wrote the original was named Bourne, so was the clone Bourne-Again Shell—commonly referred to as bash.