

\documentstyle[11pt,fancyheadings]{article}

\setlength{\oddsidemargin}{0in}
\setlength{\evensidemargin}{0in}
\setlength{\topmargin}{0.0in}
\setlength{\headheight}{0in}
\setlength{\headsep}{0.5in}
\setlength{\textwidth}{6.5in}
\setlength{\textheight}{8.5in}
\setlength{\parindent}{0in}
\setlength{\parskip}{0.1in}

\begin{document}

\pagestyle{fancy}
\cfoot{OS Program Loading and Execution: \thepage}   

\title{Role of the Operating System in Program Loading and Execution}

\author{N.S. Matloff}

\date{May 8, 1996}

\maketitle

\section{Program Loading}

Suppose you have just compiled a program, producing 
(say for a Unix system) an executable file a.out.  To run it, you type

\begin{verbatim}
% a.out
\end{verbatim}

For the time being, assume a very simple machine/OS combination,
with no virtual memory, timesharing, etc.  Here is what will occur:

\begin{itemize}
\item [1.] This command would consist of six keystrokes (including the
carriage return).  Each one causes an interrupt.  The interrupt vector
has been set up to point to a subroutine in the OS, which will
accumulate the characters (a,.,o,u,t,RET) in a buffer (simply an array
in the subroutine).  Upon seeing the RET, the routine will realize
that a complete command is now ready, and it will then call the OS
loader program.

\item [2.] The loader will look in the disk directory, to determine where
on disk the file a.out is.

\item [4.] The loader will check its memory-allocation table (again, this
is just an array in the loader routine) to find an unused region or
regions of memory large enough for a.out.  Note that we need
space both for the program's instructions and static data items (scalar
and array variables), as well as for stack space and the like.

\item [5.] The loader will then load a.out into that area of memory,
and will update its memory-allocation table accordingly.  

\item [6.] The loader will also check a certain section of the a.out
file, in which the linker previously recorded the program's {\it entry
point}, i.e. the instruction in a.out at which execution is to begin.  We 
will assume here, for the time being at least, that this entry point is 
expressed as an offset from the beginning of the program.\footnote{You
might wonder why this offset is not necessarily 0.  To see why not,
consider for example a C source file in which functions x(), y() and
main() are declared, in that order.  The entry point would be at
main(), and since that function was not the first one declared in
the C source file, the entry point offset will be nonzero.}

\item [7.] The OS is now ready to initiate execution of the program.
It will choose a free area of memory for the program's tack, and
set the stack pointer to point there.  Then it will actually start 
the program, say by executing a JMP instruction (or equivalent)
to jump to the program's entry point.

\item [8.] Eventually a.out finishes execution.  At the finish point,
the compiler has inserted instructions which make a system call to
the OS, with the system service in this case being ``end program.''
The OS will clean some things up, and then write the usual user
prompt (say \%) to the screen.
\end{itemize}

Note carefully 

\begin{itemize}
\item When the transfer of control occurs in step 7 above---from the
OS program to the a.out program---the CPU has no idea that one program
has stopped and another program has started.  The CPU has a very
simple (and simple-minded) job:  it repeatedly instruction-fetch,
instruction-execute, instruction-fetch, instruction-execute, etc.,
etc., etc.  The JMP instruction above will change the PC to point
to a new program, but the CPU does not realize this; it simply fetches
whatever instruction the PC points to.

\item Once the program a.out starts running, the OS is completely
powerless, \underline{dead}.  If the program, say, goes into an
infinite loop, the OS cannot magically ``step in'' and kill the
program; the OS cannot even ``watch'' for this, as the OS is not
running---a.out is running.  Again, the CPU executes whatever
instructions the PC points to.
\end{itemize}


\section{System Calls}

The OS provides a large number of services to user programs.  For
example, consider your calls to scanf() in your C source file.  The
scanf() function itself does not include code that directly accesses
the keyboard, nor for that matter does it include code that directly
accesses the keyboard buffer in memory.  Instead, scanf() will make
a call to a function read() within the OS, which will pick up the
keystrokes from the buffer and return them to scanf().  This is known
as a {\it system call}, meaning a call to a function within the OS
which provides some service.

There are many services which the OS provides, for I/O, process
control, and so on.  (You can get a list of them in the Unix case
by typing

\begin{verbatim}
man 2 intro
\end{verbatim}

The list appears near the end of this man page.)

Since a system call is actually just a regular subroutine call, one
might use a CALL instruction.  However, in most cases, a special
instruction, say TRAP, will be used instead.\footnote{An example is
the INT instruction from the Intel CPU family.}  These are sometimes
called {\it software interrupts}, because they have actions similar
to interrupts, but comprise software, i.e. they are
\underline{instructions,} rather than external electrical pulses.

The reason for using TRAP (or equivalent) instead of CALL is that
we typically do not know where a given OS function will be in memory.
Since a TRAP is an interrupt, it results in a jump to wherever the
interrupt vector points.  So, when the system is first booted up, the
OS will set the interrupt vector to point to the OS function (or
possibly a group of such functions).  In this manner, for instance,
the same .EXE file in a DOS environment can be used with different
versions of DOS; the given system function might be at different
places in memory for different versions, but the program will work
correctly regardless.

\section{Timesharing}

{\it Timesharing} involves having several programs (or even several
instances of the same program) running in what \underline{appears} to
be a simultaneous manner.  If the system has only one CPU, then this
simultaneity is of course only an illusion, since only one program
can run at any given time, but it is a worthwhile illusion, as we
will see.

First of all, how is this illusion attained?  The answer is that we
have the programs all take turns running, with each turn being of
very short duration, say 50 milliseconds.  Say we have four programs,
named u, v, x and y, running currently.  What will happen is that
first u runs for 50 milliseconds, then u is suspended and v runs for
50 milliseconds, then v is suspended and x runs for 50 milliseconds,
and so on (after y gets its turn, then u gets a second turn, etc.).
Since the turn-switching is happening so fast (every 50 milliseconds),
it appears to us humans that each program is running continuously
(though at one-fourth speed), rather than on and off, on and off, 
etc.\footnote{Imagine a light which turns on and off, on and off,
etc. extremely rapidly.  It will appear to us that the light is on
continuously, but at a less-than-normal brightness.}

But how can the OS enforce these turns.  For example, how can the
OS force the program u above to stop after 50 milliseconds?  The
answer is, ``It can't!  The OS is dead while u is running.''
Instead, the turns are implemented via a timing device, which
emits a hardware interrupt at the proper time.  For example, we could set
the timer to emit an interrupt every 50 milliseconds.  We would
write a timer interrupt service routine, and incorporate it into
the OS.  

That latter routine would save all u's current register values,
including PSR (Program Status Register, which contains the N, Z,
V and C bits, on which conditional branches depend).  When u's
next turn comes, those values will be restored, and u will resume
execution as if nothing ever happened.

\section{The Use of Virtual Memory for Memory Management and Protection}

Now let us add in the effect of virtual memory (VM).  We will assume 
that you have learned about VM structures, e.g. page tables, elsewhere, 
and will concentrate on the interaction of the OS with the VM hardware.

As you know, every memory address generated by the currently-running
program will be checked by the VM hardware.  The address will be
converted to a page number, and that number will serve as an index
to the page table (or, in most systems, to the translation lookaside
buffer, which acts as a special cache for the page table).  The
page table will tell us two pieces of information:

\begin{itemize}
\item [(a)] Whether this page is currently in main memory.

\item [(b)] Whether the currently-running program is allowed to make
the requested access (read, write, execute) to this page.
\end{itemize}

If the answer to either of these questions is no, the VM hardware
will generate an internal trap, and of course the trap service routine
will be the OS.  The OS will in case (a) go to disk, bring in the
requested page (whose address will also be stored in the page table),
write back the replaced page if necessary, and update the page table.
In case (b) the OS will announce an error (in Unix, something like
``segmentation fault'' or ``bus error''), and then go to the
command-reader portion of the OS, printing out the familiar
`\%' prompt.

And of course, at the time the program was loaded from disk
originally, the OS set the page table entries then too.

\section{System Bootup}

The remaining question is, ``How does the OS itself get loaded into
memory and its execution begun?''  The process by which this is done
is called {\it bootup}.

The CPU hardware will be designed so that upon powerup the PC is
initialized to some specific value, say 0xfffffff0.  And those who
{\it fabricate} the computer (i.e. who put together the CPU,
memory, bus, etc. to form a complete system) will include a small
ROM at that same address, again say 0xfffffff0.  The contents of
the ROM are the {\it boot loader} program.  So, immediately after
powerup, the boot loader program is running!

The goal of the boot loader is to load in the OS from disk to memory.
In the simple form, the boot loader reads a specified area of the
disk, copies the contents there (which will be the OS) to memory,
and then finally does a JMP to that section of memory---so that now
the OS is running.  In a more complicated form, the boot loader only
reads \underline{part} of the OS into memory, and then JMP's to the
OS; the OS then reads the rest of itself into memory.  In either
case, the OS will finally print the user prompt to the screen, and
wait for commands.

Note that the boot loader will load \underline{whatever} is at the
specified location on disk.  Thus we could run a different OS,
simply by replacing that part of the disk by the new OS.

\end{document}

