Page 427 - DCAP103_Principle of operating system
P. 427
Principles of Operating Systems
Notes Once moved, boot reads the root directory of the boot device. To do this, it must understand
the file system and directory format, which is the case with some bootloaders such as GRUB
BootloaderGRandUnified Other popular bootloaders, such as Intel’s LILO, do not rely on any
specific filesystem. Instead, they need a block map, and low-level addresses, which describe
physical sectors, heads, and cylinders, to find the relevant sectors to be loaded. Then it reads
in the operating system kernel and jumps to it. At this point, boot has finished its job and the
kernel is running.
The kernel start-up code is written in assembly language and is highly machine dependent.
Typical work includes setting up the kernel stack, identifying the CPU type, calculating the
amount of RAM present, disabling interrupts, enabling the MMU, and finally calling the
C-language main procedure to start the main part of the operating system. The C code also
has considerable initialization to do, but this is more logical than physical. It starts out by
allocating a message buffer to help debug boot problems. As initialization proceeds, messages
are written here about what is happening, so they can be fished out after a boot failure by a
special diagnostic program. Think of this as the operating system’s cockpit flight recorder (the
black box investigators look for after a plane crash). Next the kernel data structures are allocated.
Most are fixed size, but a few, such as the page cache and certain page table structures, depend
on the amount of RAM available.
At this point the system begins autoconfiguration. Using configuration files telling what kinds of
I/O devices might be present, it begins probing the devices to see which ones actually are present. If
a probed device responds to the probe, it is added to a table of attached devices. If it fails to respond,
it is assumed to be absent and ignored henceforth. Unlike traditional UNIX versions, Linux can device
drivers do not need to be statically linked and may be loaded dynamically (as can all versions of
MS-DOS and Windows, incidentally). The arguments for and against dynamically loading
drivers are interesting and worth stating briefly. The main argument for dynamic loading
is that a single binary can be shipped to customers with divergent configurations and have
it automatically load the drivers it needs, possibly even over a network. The main argument
against dynamic loading is security. If you are running a secure site, such as a bank’s database
or a corporate Web server, you probably want to make it impossible for anyone to insert random
code into the kernel. The system administrator may keep the operating system sources and
object files on a secured machine, do all system builds there, and ship the kernel binary to
other machines over a local area network. If drivers cannot be loaded dynamically, this scenario
prevents machine operators and others who know the superuser password from injecting
malicious or buggy code into the kernel. Furthermore, at large sites, the hardware configuration
is known exactly at the time the system is compiled and linked. Changes are sufficiently rare
having to relink the system when a new hardware device is added is not an issue.
Once all the hardware has been configured, the next thing to do is to carefully handcraft process 0,
set up its stack, and run it. Process 0 continues initialization, doing things like programming the
real-time clock, mounting the root file system, and creating init (process 1) and the page daemon
(process 2). Init checks its flags to see if it is supposed to come up single user or multiuser.
In the former case, it forks off a process that execs the shell and waits for this process to exit.
In the latter case, it forks off a process that executes the system initialization shell script, /etc/
rc, which can do file system consistency checks, mount additional file systems, start daemon
processes, and so on. Then it reads /etc/ttys, which lists the terminals and some of their
properties. For each enabled terminal, it forks off a copy of itself, which does some housekeeping
and then execs a program called getty. Getty sets the line speed and other properties for each
line (some of which may be modems, for example), and then types login: on the terminal’s screen
and tries to read the user’s name from the keyboard. When someone sits down at the terminal
and provides a login name, getty terminates by executing /bin/login, the login program. Login
then asks for a password, encrypts it, and verifies it against the encrypted password stored in
the password file, /etc/passwd. If it is correct, login replaces itself with the user’s shell, which
then waits for the first command. If it is incorrect, login just asks for another user name. This
mechanism is illustrated in Figure 14.11 for a system with three terminals.
420 LOVELY PROFESSIONAL UNIVERSITY