view docs/nitros9guide/chap5.chapter @ 3116:174eb9eda7b1

New port "mc09" for Multicomp09, an FPGA-based 6809 machine Include new commands/descriptor for mc09 clock and SD controller
author Neal Crook <foofoobedoo@gmail.com>
date Sat, 17 Oct 2015 21:21:11 +0100
parents b00cf13c9f61
children
line wrap: on
line source

<chapter>
<title>Multiprogramming and Memory Management</title>
<para>
One of NitrOS-9's most extraordinary abilities is multiprogramming,
which is sometimes called timesharing or multitasking. Simply
states, NitrOS-9 lets you computer run more than one program at the same
time. This can be a tremendous advantage in many situations. For
example, you can be editing one program while another is being
printed. Or you can use your Color Computer to control household
automation and still be able to use it for routine work and
entertainment.
</para>
<para>
NitrOS-9 uses this capability all the time for internal functions.
The simple way for you to do so is by putting a &quot;&amp;&quot; character at the
end of a command line which causes the shell to run your command as
a &quot;background task&quot;.
</para>
<para>
The information presented in this chapter is intended to give you
an insight into how NitrOS-9 performs this amazing feat. You certainly
don't have to know every detail of how multiprogramming works in
order to use NitrOS-9, but a basic working knowledge can help you
discover many new ways to use your Color Computer.
</para>
<para>
In order to allow several programs to run simultaneously and
without interference, NitrOS-9 must perform many coordination and
resource allocation functions. The major system resources managed
by NitrOS-9 are:
</para>
<simplelist>
<member>CPU Time</member>
<member>Memory</member>
<member>The input/output system</member>
</simplelist>
<para>
In order for the computer to have reasonable performance, these
resources must be managed in the most efficient manner possible.
Therefore, NitrOS-9 uses many techniques and strategies to optimize
system throughput and capacity.
</para>

<section id="sec5.1">
<title>Processor Time Allocation and Timeslicing</title>

<para>
CPU time is a resource that must be allocated wisely to maximize
the computer's throughput. It is characteristic of many programs to
spend much unproductive time waiting for various events, such as an
input/output operation. A good example is an interactive program
which communicates with a person at a terminal on a line-by line
basis. Every time the program has to wait for a line of characters
to be typed or displayed, it (typically) cannot do any useful
processing and would waste CPU time. An efficient multiprogramming
operating system such as NitrOS-9 automatically assigns CPU time to only
those programs that can effectively use the, time.
</para>
<para>
NitrOS-9 uses a technique called <emphasis>timeslicing</emphasis> which allows processes
to share CPU time with all other active processes. Timeslicing is
implemented using both hardware and software functions. The
system's CPU is interrupted by a real time clock many (60 in the
Color Computer) times each second. This basic time interval is
called a &quot;tick&quot;, hence, the interval between ticks is a time slice.
This technique is called timeslicing because each second of CPU time
is sliced up to be shared among several processes. This happens so
rapidly that to a human observer all processes appear to execute
continuously, unless the computer becomes overloaded with processing. If this
happens, a noticeable delay in response to terminal
input may occur, or &quot;batch&quot; programs may take much longer to run
than they ordinarily do. At any occurrence of a tick, NitrOS-9 can suspend
execution of one program and begin execution of another. The
starting and stopping of programs is done in a manner that does not
affect the program's execution. How frequently a process is given
time slices depends upon its assigned priority relative to the
assigned priority of other active processes.
</para>
<para>
The percentage of CPU time assigned to any particular process
cannot be exactly computed because there are dynamic variables such
as time the process spends waiting for I/O devices. It can be
roughly approximated by dividing the process's priority by the sum
of the priority numbers of all processes:
</para>
<screen>

                     Process Priority
Process CPU Share = -------------------
                     Sum of All Active
                    Process' Priorities
</screen>
</section>

<section id="sec5.2">
<title>Process States</title>

<para>
The CPU time allocation system automatically assigns programs one
of three &quot;states&quot; that describe their current status. Process
states are also important for coordinating process execution. A
process may be in one and only one state at any instant, although
state changes may be frequent. The states are:


</para>
<para>
<emphasis>ACTIVE:</emphasis>
processes which can currently perform useful processing.
These are the only processes assigned CPU time.
</para>
<para>
<emphasis>WAITING:</emphasis>
processes which have been suspended until another process
terminates. This state is used to coordinate execution of
sequential programs. The shell, for example, will be in the waiting
state during the time a command program it has initiated is running.

</para>
<para>
<emphasis>SLEEPING:</emphasis>
processes suspended by self-request for a specified time
interval or until receipt of a &quot;signal&quot;. Signals are internal
messages used to coordinate concurrent processes. This is the
typical state of programs which are waiting for input/output
operations.

</para>
<para>

Sleeping and waiting processes are not given CPU time until they
change to the active state.
</para>
</section>

<section id="sec5.3">
<title>Creation of New Processes</title>

<para>
The sequence of operations required to create a new process and
initially allocate its resources (especially memory) are
automatically performed by NitrOS-9's &quot;fork&quot; function. If for any
reason any part of the sequence cannot be performed the fork is
aborted and the prospective parent is passed an appropriate error
code. The most frequent reason for failure is unavailablity of
required resources (especially memory) or when the program specified
to be run cannot be found. A process can create many new processes,
subject only to the limitation of the amount of unassigned memory
available.
</para>
<para>
When a process creates a new process, the creator is called the
&quot;parent process&quot;, and the newly created process is called the &quot;child
process&quot;. The new child can itself become a parent by creating yet
another process. If a parent process creates more than one child
process, the children are called &quot;siblings&quot; with respect to each
other. If the parent/child relationship of all processes in the
system is examined, a hierarchical lineage becomes evident. In
fact, this hierarchy is a tree structure that resembles a family
tree. The &quot;family&quot; concept makes it easy to describe relationships
between processes, and so it is used extensively in descriptions of
NitrOS-9's multiprogramming operations.
</para>
<para>
When the parent issues a fork request to NitrOS-9, it must specify
the following required information:
</para>
<itemizedlist mark="square">
<listitem><para>
A PRIMARY MODULE, which is the name of the program to be
executed by the new process. The program can already be present
in memory, or NitrOS-9 may load it from a mass storage file having
the same name.
</para></listitem>
<listitem><para>
PARAMETERS, which is data specified by the parent to be
passed to and used by the new process. This data is copied to
part of the child process' memory area. Parameters are
frequently used to pass file names, initialization values, etc.
The shell, passes command line parameters this way.
</para></listitem>
</itemizedlist>
<para>
The new process also &quot;inherits&quot; copies of certain of its parent's
properties. These are:
</para>
<itemizedlist mark="square">
<listitem><para>
A USER NUMBER which is used by the file security system and
is used to identify all processes belonging to a specific user
(this is not the same as the &quot;process ID&quot;, which identifies a
specific process) . This number is usually obtained from the
system password file when a user logs on. The system manager
always is user number zero.
</para></listitem>
<listitem><para>
STANDARD INPUT AND OUTPUT PATHS: the three paths (input,
output, and error/status) used for routine input and output.
Note that most paths (files) may be shared simultaneously by
two or more processes. The two current working
directories are also inherited.
</para></listitem>
<listitem><para>
PROCESS PRIORITY which determines what proportion of CPU
time the process receives with respect to others.
</para></listitem>
</itemizedlist>
<para>
As part of the fork operation, NitrOS-9 automatically assigns:
</para>
<itemizedlist mark="square">
<listitem><para>
A PROCESS ID: a number from 1 to 255, which is used to
identify specific processes. Each process has a unique process
ID number.
</para></listitem>
<listitem><para>
MEMORY: enough memory required for the new process to run.
Level Two systems give each process a unique &quot;address space&quot;.
In Level One systems, all processes share the single address
space. A &quot;data area&quot;, used for the program's parameters,
variables, and stack is allocated for the process' exclusive
use. A second memory area may also be required to load the
program (primary module) if it is not resident in memory.
</para></listitem>
</itemizedlist>
<para>
To summarize, the following items are given to or associated with
new processes:
</para>

<itemizedlist mark="square">
<listitem><para>
Primary Module (program module to be run)
</para></listitem>
<listitem><para>
Parameter(s) passed from parent to child
</para></listitem>
<listitem><para>
User Number
</para></listitem>
<listitem><para>
Standard I/O paths and working directories
</para></listitem>
<listitem><para>
Process Priority
</para></listitem>
<listitem><para>
Process ID
</para></listitem>
<listitem><para>
Memory
</para></listitem>
</itemizedlist>

</section>

<section id="sec5.4">
<title>Basic Memory Management Functions</title>
<para>
An important NitrOS-9 function is memory management. NitrOS-9 automatically allocates
all system memory to itself and to processes, and
also keeps track of the logical <emphasis>contents</emphasis>
of memory (meaning which
program modules are resident in memory at any given time). The
result is that you seldom have to be bothered with the actual memory
addresses of programs or data.
</para>
<para>
Within the address space, memory is assigned from higher
addresses  downward  for  program  modules, and from lower addresses
upward for data areas, as shown below:
</para>
<screen>
                +---------------------------+  highest address
                !       program modules     !
                !         (RAM or ROM)      !
                !                           !
                ! - - - - - - - - - - - - - !
                !                           !
                !        unused space       !
                !       (RAM or empty)      !
                !                           !
                ! - - - - - - - - - - - - - !
                !                           !
                !         data areas        !
                !           (RAM)           !
                !                           !
                +---------------------------+  lowest address (0)
</screen>
<section id="sec5.4.1">
<title>Loading Program Modules Into Memory</title>

<para>
When performing a fork operation, NitrOS-9's first step is to attempt
to locate the requested program module by searching the &quot;module
directory&quot;, which has the address of every module present in memory.
The &CPU; instruction set supports a type of program called
&quot;reentrant code&quot; which means the exact same &quot;copy&quot; of a program can
be shared by two or more different processes simultaneously without
affecting each other, provided that each &quot;incarnation&quot; of the
program has am independent memory area for its variables.
</para>
<para>
Almost all NitrOS-9 family software is reentrant and can make most
efficient use of memory. For example, Basic09 requires 22K bytes of
memory to load into. If a request to run Basic09 is made, but
another user (process) had previously caused it to be loaded into
memory, both processes will share the same copy, instead of causing
another copy to be loaded (which would use an additional 22K of
memory). NitrOS-9 automatically keeps track of how many processes are
using each program module and deletes the module (freeing its memory
for other uses) when all processes using the module have terminated.
</para>
<para>
If the requested program module is not already in memory, the
name is used as a pathlist (file name) and an attempt is made to
load the program from mass storage.
</para>
<para>
Every program module has a &quot;module header&quot; that describes the
program and its memory requirements. NitrOS-9 uses this to determine
how much memory for variable storage should be allocated to the
process (it can be given more memory by specifying an optional
parameter on the shell command line). The module header also
includes other important descriptive information about the program,
and is an essential part of NitrOS-9 operation at the machine language
level. A detailed description of memory modules and module headers
can be found in the &quot;NitrOS-9 System Programmer's Manual&quot;.
</para>
<para>
Programs can also be explicitly loaded into memory using the
<command>load</command> command. As with fork, the program will actually be loaded
only if it is not already in memory. If the module is not in
memory, NitrOS-9 will copy a candidate memory module from the file into
memory, verify the CRC, and then, if the module is not already in
the module directory, add the module to the directory. This process
is repeated until all the modules in the file are loaded, the 64K
memory limit is exceeded, or until a module with an invalid format
is encountered. NitrOS-9 always links to the first module read from the
file.
</para>
<para>
If the program module <emphasis>is</emphasis> already in memory,
the load will proceed
as described above, loading the module from the specified file,
verifying the CRC, and when attempting to add the valid module to
the module directory, noticing that the module is already known, the
load merely increments the known module's link count (the number of
processes using the module.) The load command can be used to &quot;lock
a program into memory. This can be useful if the same program is to
be used frequently because the program will be kept in memory
continuously, instead of being loaded repeatedly.
</para>
<para>
The opposite of <command>load</command> is the <command>unlink</command> command, which decreases a
program module's link count by one. Recall that when this count becomes zero
(indicating the module in no longer used by any process),
the module is deleted, e.g., its memory is deallocated and its name
is removed from the module directory. The <command>unlink</command> command is
generally used in conjunction with the <command>load</command> command (programs
loaded by fork are automatically unlinked when the program
terminates).
</para>
<para>
Here is an example of the use of
<command>load</command> and <command>unlink</command> to lock a
program in memory. Suppose the <command>copy</command> command will be used five
times. Normally, the copy command would be loaded each time the
<command>copy</command> command is called. If the <command>load</command> command is used first,
<command>copy</command> will be locked into memory first, for example:
</para>
<screen>
OS9: load copy
OS9: copy file1 file1a
OS9: copy file2 file2a
OS9: copy file3 file3a
OS9: unlink copy
</screen>
<para>
It is important to use the <command>unlink</command> command after the program is no
longer needed, or the program will continue to occupy memory which
otherwise could be used for other purposes. Be very careful
<emphasis>not</emphasis> to
completely unlink modules in use by any process! This will cause the
memory used by the module to be deallocated and its contents
destroyed. This will certainly cause all programs using the
unlinked module to crash.
</para>
</section>

<section id="sec5.4.2">
<title>Loading Multiple Programs</title>

<para>
Another important aspect of program loading is the ability to
have two or more programs resident in memory at the same time. This
is possible because all NitrOS-9 program modules are &quot;position-independent
code&quot;, or &quot;PIC&quot;. PIC programs do not have to be loaded into
specific, predetermined memory addresses to work correctly, and can
therefore be loaded at different memory addresses at different
times. PIC programs require special types of machine language instructions
which few computers have. The ability of the &CPU;
microprocessor to use this type of program is one of its most
powerful features.
</para>
<para>
The <command>load</command> command can therefore be used two or more times (or a
single file may contain several memory modules), and each
program module will be automatically loaded at different,
non-overlapping addresses (most other operating systems write over the
previous program's memory whenever a new program is loaded). This
technique also relieves the user from having to be directly concerned with
absolute memory addresses. Any number of program modules
can be loaded until available system memory is full.
</para>
</section>

<section id="sec5.4.3">
<title>Memory Fragmentation</title>

<para>
Even though PIC programs can be initially loaded at any address
where free memory is available, program modules cannot be relocated
dynamically afterwards, e.g., once a program is loaded it must
remain at the address at which it was originally loaded (however
Level Two systems can &quot;load&quot; (map) memory resident programs at
different addresses in each process' address space). This characteristic
can lead to a sometimes troublesome phenomenon called
&quot;memory fragmentation&quot;. When programs are loaded, they are assigned
the first sufficiently large block of memory at the highest address
possible in the address space. If a number of program modules are
loaded, and subsequently one or more modules which are located in
between other modules are &quot;unlinked&quot;, several fragments of free
memory space will exist. The sum of the sizes of the free memory
space may be quite large, but because they are scattered, not enough
space will exist in a single block to load a program module larger
than the largest free space.
</para>
<para>
The <command>mfree</command> command shows the location and size of each unused
memory area and the <command>mdir -e</command> command shows the address, size, and
link (use) count of each module in the address space. These
commands can be used to detect fragmentation. Memory can usually be
de-fragmemted by unlinking scattered modules and reloading them.
<emphasis>Make certain</emphasis> none are in use before doing so.
</para>
</section>
</section>
</chapter>