Sun Jan 22, 2017
When the computer is powered up, after the initial hardware scan and gathering, the BIOS then checks which device contains the boot information. This could be a floppy disk, CD-ROM, or typically, the primary hard drive. The first sector (bootsector) is read. The small program in this area reads a partition table that is kept at the end of the sector. This table has all of the hard drives, their partitions, and their purpose, listed. The one it looks for, specifically, is the one designated as the boot partition. Then, this little program turns over control to another program, called the bootloader, loading it from that boot partition and subsequently running it.
This bootloader is where things really start getting interesting, especially when it comes to the operating system. This is also where the first significant difference between monolithic kernels (such as UNIX and Linux) and microkernels arises.
For Linux, the bootloader must access a saved configuration file as well as the kernel. To do this it must have its own built-in hard disk and filesystem drivers. With these it locates the configuration file and the kernel, also on the boot partition. Once the bootloader locates these, it loads the kernel into memory and runs it according to specialized settings mentioned in the configuration file.
The kernel does a preliminary CPU and RAM scan to know what it has to work with and then sets up all of its own memory: for the kernel stack, debug messages, data structures, etc. After this, Linux then begins its autoconfiguration where it sets up all of the I/O devices. It gets the data from the system, as far as which devices are installed and what their interrupts and address spaces are, and does its own scan to those devices, building its own table of what is installed. Linux then steps through this new table of installed devices and loads the appropriate driver to be able to speak to the device and poles each with a quick initialization routine.
After this painstaking hardware setup, Linux does a little further housework such as initializing the realtime clock, and mounting the root filesystem. Then, Linux starts the creation of the first official process, called
init. To run this, it does what it does with every other program it’s about to run: sets up
init’s stack, and then points the instruction pointer (the place marker for the processor which points to the next instruction to execute) to the beginning of init.
Init’s first job is running the initialization script to set up user level servers (such as networking services, mailserver daemons, etc.). Init also creates/runs the shells which the user (and other programs) use to talk to the computer. At this point the bootup process is nearly completed, unless a Graphical User Interface (GUI), such as X Windows, is run.
Due to Qubit’ microkernel design, there are many things it cannot do on its own. Because of this, it requires a little more from the bootloader other than just a simple run command.
One major part of the supposed micro-kernel handicap is its inability to talk to any of the devices, such as the hard disk. This is a slight problem because the kernel needs to somehow have access to the first few programs it will run, such as disk and filesystem drivers! To avoid this chicken and egg problem the kernel takes advantage of the bootloader’s built-in disk-access feature.
Stored on the same small boot partition as the bootloader’s configuration file and the kernel are these few small setup and initialization programs, which the kernel will run. The bootloader, when loading the kernel into memory, also loads these small setup programs into memory and saves their begin and end addresses into a simple data structure called the multiboot header. When the bootloader surrenders its execution to the kernel, it passes along the address of the header as an argument.
The kernel reads the multiboot header, setting aside the addresses for the programs. It then takes the BIOS’s hardware interrupt information and records them. Next, the kernel maps out its working memory, initializes the virtual memory management, and allocates memory for the process and thread data structures. The kernel then sets up process and thread information for the actual setup programs, flags them as runnable and turns over control to the external processes.
One of these setup programs creates a lookup table for service names; another is the device driver for the hard drive; one is the filesystem driver; and the last one is the init. Although they can be loaded in any order, their execution still happens in a predetermined sequence because each will wait or block, until the one it needs is ready. For instance, init needs to be able to read off of the filesystem, so it’ll block until that program is running. However, the filesystem needs to be able to read the hard disk, but the disk needs a place to store the addresses of its service calls for others to find. So, when all of these are loaded, they start in order, like dominoes:
- namer - the service lookup table.
- wd - the hard disk driver.
- dosfs - the DOS filesystem driver.
- init - main initialization.
Whereas Linux initialized and set up all of the devices and their drivers, the root filesystem, etc., inside the kernel, Qubit leaves most of that up to modules outside of the kernel, in user space. This is just the first of many neat and nifty things that Qubit does differently that set it apart from other operating systems.