From oleg Tue Sep 13 09:43:22 CDT 1994 Newsgroups: comp.lang.c++ Subject: C++ *is* good for writing OS kernels. Here's an example. Summary: Announcement of the toy_OS written in full C++ Followup-To: Distribution: Organization: University of North Texas, Denton Keywords: OS kernel, systems programming, threads, multiprocessing Cc: Status: RO Sorry for starting with the platitude, but I've always taken it for granted that C++ and OOP in general are very well suited for operating systems design, OS kernels in particular. However, I was amazed to come across some people who contend that C++ adds too much overhead, and, in general, the kernel of OS is not something where classes, objects, (multiple) inheritance, etc would contribute much. So, if one were to write an OS kernel in C++, he would do it in a C-like style, maybe occasionally using function overloading and minor things like that. Design of nachos seems to confirm this point. I for one strongly disagree. When I took an OS class, I had a bet with my instructor that I would write an OS in *full* C++ and show him that the OS kernel could be very well and efficiently represented as a collection of C++ objects. And I did. As usual in the class projects like that, the instructor has provided us with some OS code written in C and with the emulator of the hardware the OS "operates" on. The OS is supposed to run as a single process under UNIX, with interrupts, system/user states etc asynchronous things emulated through the thread library. I wrote everything including the hardware emulator (but the thread library) myself, from scratch, without even looking at the instructor's code. C++ was used in its full grace, with _very_ multiple inheritance (sometimes to the point of breaking the compiler), static class objects, virtual classes, operator/function overloads, etc. The code is _well_ commented! I tried to make the OS as fool-proof as possible, so as to minimize the probability of a misbehaved/malicious process crashing the OS or seriously affecting other processes. Highlights: UNIX-like OS kernel with semaphores, virtual memory and asynchronous I/O, hardware emulator, "bus" paradigm for both OS and hardware (classes), extensibility to multi-processor architecture, C++ in full grace. The operating system kernel supports a minimal set of multiprocessing system calls like fork, wait, P and V semaphore operations, and I/O initiation. Process scheduling is round-robin with fixed time quants. A memory manager handles page faults and provides address translation (for channel instructions), page locking/unlocking, page reservation, etc. common memory services. Two different page replacement strategies were implemented and compared: "swap out" and "local LRU" (with working set quotas like those in VAX/VMS). I ran a suite of test "jobs" and compared the number of page faults, etc. There is a report as to how the two strategies fare (it wasn't actually a part of the class project, I did it just for the heck of it). The I/O is asynchronous, that is, an I/O processor (channel) runs concurrently with the main CPU, there can be several active i/o requests, and many more may be submitted simultaneously. There are extensive recording facilities, which print the status of the system and different units as the system runs. The "hardware" looks suspiciously like IBM/370, though with pure page virtual memory (not page-segmented), but with several "I/O channels" that understand their own set of "channel commands" and run asynchronously with the main CPU. As crazy and wayward as my approach was (from the instructor's point of view), it still was mandatory to come up with something that is able to run the test "jobs" the instructor designed. I'm sorry I had no choice but to implement the hardware architecture, instruction set, system call set, etc the project required, however quaint they are. The hardware emulator is made of classes Memory, MemoryManagementUnit (to handle the virtual memory), CPU, IOBus, IOChannel, and devices LinePrinter, CardReader (so quaint -:), and the HardDisk. The entire project makes a great deal of use of a "bus architecture" paradigm. Say, on the hardware side, there is a class "Computer" that holds all units and gives necessary references from one unit to another at the "construction time". This format makes it a snap to have a computer with two CPUs "connected" to a single memory bank, two CPUs with two memory banks, etc. BTW, there is a class Context that holds all CPU registers and other control info. The class CPU is based on Context, so is a class PCB (Process Control Block). So, it makes saving/restoring of the CPU context during a process switch look as a simple assignment. Operating System classes are built upon the hardware classes, moreover, they mirror the hardware classes. Say, MemoryManager is based on Memory, CPUManager is based on the CPU (as well as on ProcessTable, etc), IOChannelManager is based on the IOChannel. So, the OS is considered and designed as an "extension" of the hardware. The class OperatingSystem is based on the class Computer. It holds all managers and provides for their mutual references, so it's kind of "software bus". BTW, OperatingSystem is the *only* object created (in the module bootstrap). All other components are "physically" parts of the OperatingSystem object and know of each other through the "bus". It's only recently occurred to me that building OS classes on the top of the "hardware" classes abstracts hardware specifics a great deal, and makes the OS design rather portable. Really, say, to resume the execution of a user process, the kernel class CPUMananger in one of his methods calls CPU.load_context() and CPU.start(). The CPUManager could care less as to how the latter functions actually work. In my project, they load registers of the emulated CPU and switch threads (thus switching from the system space to the user space). If the kernel is to run on a real hardware, the only thing one needs to do is to implement CPU class methods using real "return from interrupt" and other appropriate machine instructions. Also, the OS kernel is in a sense a vastly bloated thread library. By trimming down virtual memory management, semaphore ownership checking, etc (and adding stack allocation/switching, which is specific to the environment), the OS kernel can easily be turned into a thread library. The entire source code of the toy OS is archived in comp.sources.misc. Please see the README file as to the credits, history, more details on the implementation, etc. File OS.dr lists all the source code files and tells briefly what they're for. I did this project two years ago, so thinking back, I would've done quite a few things differently. Besides, I spent only 3 weeks (though pretty tough 3 weeks) implementing the software and "hardware". I even repeated my personal record of 2,000 lines of *working* C++ code per week. This is to say that the code is not as polished as I wanted to. Anyway, I guess I proved, at least to myself, that C++ in its full grace is quite useful (and efficient) for the OS design. As to the OS development, well, I still have hankering for it. So if something in this project turns out to be useful in any respect, but some changes/rework seems necessary, well, I can do it (not overnight, of course, but I'll try -:) If you wish to comment, please mail me at oleg@ponder.csci.unt.edu or oleg@unt.edu.