When a system is powered on (a cold start), some tiny program in nonvolatile storage (e.g., PROM) is executed which will look for a bootstrap program, named boot, to load into the CPU. In some cases, the tiny program is the bootstrap program itself.
The boot program may reside in a specific location in the filesystem or is loaded from a storage device (e.g., floppy).
The boot program reads the binary image of a program, in this case the UNIX kernel, to be bootstrapped into the main memory and then initializes the CPU so that the loaded program can be started.
The UNIX kernel, in many UNIXes, is stored in the filesystem as the file /unix or /vmunix. The boot program is capable of reading the UNIX filesystem, or at least the first few sectors where the kernel is to be found.
Boot always loads the UNIX kernel at memory location 0.
When the UNIX kernel is started by the boot program, it does an initialization in preparation for the execution of application programs, in three stages, roughly:
Assembly-language startup. Very low-level actions, hence written in assembly language. The actions include:
Machine-dependent initialization. After the assembly-language code has completed its work, it calls the first kernel routine written in C, main(). The tasks of the machine-dependent startup code include
Machine-independent initialization. With the static system data structures allocated and the I/O devices configured and initialized, the system is ready to complete the initialization procedure and to start up the first few processes.
The first process, process 0, performs a series of initialization, including the paging system, the disk quota resource-control system, the real-time clock, the network interfaces, and the many tables for the filesystem and its cache. Process 0 is also called the swapper process.
Finally, the system is ready to execute user-mode programs. Process 1 and process 2 (which is the pagedaemon) are created.
With the start of execution of process 1, the init process, most of the system is operating and functional. Before the user sees the login prompt, the following take place.
SunOS 5.6 login:
Once the system is steadily running, the situation is like what is in Figure 4.? of your textbook, where all user processes are descendents of the init process.
A running process calls fork() to create a "clone" of itself. But what is a clone of a process good for? fork() without exec() is of no practical use at all.
Note that process 0 must be "crafted" because before it there exists no process.
The exec() system call overlays the calling process with a designated program, as follows.
The two combined forms a handy way for creating a process.
Here is a simple C++ program that creates a child process:
#include <iostream.h> #include <sys/types.h> #include <unistd.h> pid_t fork(void); main() { if (fork() == 0) { // This is the child cout << "I am the clone\n"; execl("/usr/bin/date", "date", NULL); } else { // This is the parent cout << "I am the parent\n"; // ... do what the parent is supposed to do } }
When the program is executed, it become a process, the parent process. When fork() is called, a child process is created; either process can use the return value of the call to identify itself. The child process then turns itself into a different process by replacing itself by the binary (a.out) of the date command.