Memory Management - CSC 205
Memory Management - CSC 205
In a uniprogramming system, main memory is divided into two parts: one part for the operating
system (resident monitor, kernel) and one part for the program currently being executed. In a
multiprogramming system, the “user” part of memory must be further subdivided to
accommodate multiple processes. The task of subdivision is carried out dynamically by the
operating system and is known as memory management.
Effective memory management is vital in a multiprogramming system. If only a few processes
are in memory, then for much of the time all of the processes will be waiting for I/O and the
processor will be idle. Thus, memory needs to be allocated to ensure a reasonable supply of
ready processes to consume available processor time.
Relocation
In a multiprogramming system, the available main memory is generally shared among a number
of processes. Typically, it is not possible for the programmer to know in advance which other
programs will be resident in main memory at the time of execution of his or her program. In
addition, we would like to be able to swap active processes in and out of main memory to
maximize processor utilization by providing a large pool of ready processes to execute. Once a
program is swapped out to disk, it would be quite limiting to specify that when it is next swapped
back in, it must be placed in the same main memory region as before. Instead, we may need to
relocate the process to a different area of memory.
Thus, we cannot know ahead of time where a program will be placed, and we must allow for the
possibility that the program may be moved about in main memory due to swapping. These facts
raise some technical concerns related to addressing, as illustrated in figure below. The figure
depicts a process image. For simplicity, let us assume that the process image occupies a
contiguous region of main memory. Clearly, the operating system will need to know the location
of process control information and of the execution stack, as well as the entry point to begin
execution of the program for this process. Because the operating system is managing memory
and is responsible for bringing this process into main memory, these addresses are easy to come
by. In addition, however, the processor must deal with memory references within the program.
Branch instructions contain an address to reference the instruction to be executed next. Data
reference instructions contain the address of the byte or word of data referenced. Somehow, the
processor hardware and operating system software must be able to translate the memory
references found in the code of the program into actual physical memory addresses, reflecting
the current
location of the program in main memory.
Protection
Each process should be protected against unwanted interference by other processes, whether
accidental or intentional. Thus, programs in other processes should not be able to reference
memory locations in a process for reading or writing purposes without permission. In one sense,
satisfaction of the relocation requirement increases the difficulty of satisfying the protection
requirement. Because the location of a program in main memory is unpredictable, it is
impossible to check absolute addresses at compile time to assure protection. Furthermore, most
programming languages allow the dynamic calculation of addresses at run time (e.g., by
computing an array subscript or a pointer into a data structure). Hence all memory references
generated by a process must be checked at run time to ensure that they refer only to the memory
space allocated to that process. Fortunately, we shall see that mechanisms that support relocation
also support the protection requirement.
Normally, a user process cannot access any portion of the operating system, neither program nor
data. Again, usually a program in one process cannot branch to an instruction in another process.
Without special arrangement, a program in one process cannot access the data area of another
process. The processor must be able to abort such instructions at the point of execution.
Note that the memory protection requirement must be satisfied by the processor (hardware)
rather than the operating system (software). This is because the OS cannot anticipate all of the
memory references that a program will make. Even if such anticipation were possible, it would
be prohibitively time consuming to screen each program in advance for possible memory-
reference violations. Thus, it is only possible to assess the permissibility of a memory reference
(data access or branch) at the time of execution of the instruction making the reference. To
accomplish this,
the processor hardware must have that capability.
Sharing
Any protection mechanism must have the flexibility to allow several processes to access the
same portion of main memory. For example, if a number of processes are executing the same
program, it is advantageous to allow each process to access the same copy of the program rather
than have its own separate copy. Processes that are cooperating on some task may need to share
access to the same data structure.
The memory management system must therefore allow controlled access to shared areas of
memory without compromising essential protection. Again, we will see that the mechanisms
used to support relocation support sharing capabilities.
Logical Organization
Almost invariably, main memory in a computer system is organized as a linear, or one-
dimensional, address space, consisting of a sequence of bytes or words. Secondary memory, at
its physical level, is similarly organized. While this organization closely mirrors the actual
machine hardware, it does not correspond to the way in which programs are typically
constructed. Most programs are organized into modules, some of which are unmodifiable (read
only, execute only) and some of which contain data that may be modified. If the operating
system and computer
hardware can effectively deal with user programs and data in the form of modules of some sort,
then a number of advantages can be realized:
1. Modules can be written and compiled independently, with all references from one
module to another resolved by the system at run time.
2. With modest additional overhead, different degrees of protection (read only, execute
only) can be given to different modules.
3. It is possible to introduce mechanisms by which modules can be shared among processes.
The advantage of providing sharing on a module level is that this corresponds to the
user’s way of viewing the problem, and hence it is easy for the user to specify the sharing
that is desired.
Physical Organization
Computer memory is organized into at least two levels, referred to as main memory and
secondary memory. Main memory provides fast access at relatively high cost. In addition, main
memory is volatile; that is, it does not provide permanent storage. Secondary memory is slower
and cheaper than main memory and is usually not volatile. Thus, secondary memory of large
capacity can be provided for long-term storage of programs and data, while a smaller main
memory holds programs and data currently in use.
In this two-level scheme, the organization of the flow of information between main and
secondary memory is a major system concern. The responsibility for this flow could be assigned
to the individual programmer, but this is impractical and undesirable for two reasons:
1. The main memory available for a program plus its data may be insufficient. In that case,
the programmer must engage in a practice known as overlaying, in which the program
and data are organized in such a way that various modules can be assigned the same
region of memory, with a main program responsible for switching the modules in and out
as needed. Even with the aid of compiler tools, overlay programming wastes programmer
time.
MEMORY PARTITIONING
The principal operation of memory management is to bring processes into main memory for
execution by the processor. In almost all modern multiprogramming systems, this involves a
sophisticated scheme known as virtual memory. Virtual memory is, in turn, based on the use of
one or both of two basic techniques: segmentation and paging. Before we can look at these
virtual memory techniques, we must prepare the ground by looking at simpler techniques that do
not involve virtual memory.
One of these techniques, partitioning, has been used in several variations in some now-obsolete
operating systems. The other two techniques, simple paging and simple segmentation, are not
used by themselves. However, to clarify the discuss on virtual memory if we look first at these
two techniques in the absence of virtual memory considerations.
Fixed Partitioning
In most schemes for memory management, we can assume that the OS occupies some fixed
portion of main memory and that the rest of main memory is available for use by multiple
processes. The simplest scheme for managing this available memory is to partition it into regions
with fixed boundaries.
PARTITION SIZES: The figure below shows examples of two alternatives for fixed
partitioning. One possibility is to make use of equal-size partitions. In this case, any process
whose size is less than or equal to the partition size can be loaded into references within the
program. Branch instructions contain an address to reference the instruction to be executed next.
Data reference instructions contain the address of the byte or word of data referenced. Somehow,
the processor hardware and operating system software must be able to translate the memory
references found in the code of the program into actual physical memory addresses, reflecting
the current location of the program in main memory.
any available partition. If all partitions are full and no process is in the Ready or Running state,
the operating system can swap a process out of any of the partitions and load in another process,
so that there is some work for the processor.
There are two difficulties with the use of equal-size fixed partitions:
• A program may be too big to fit into a partition. In this case, the programmer must design the
program with the use of overlays so that only a portion of the program need be in main memory
at any one time. When a module is needed that is not present, the user’s program must load that
module into the program’s partition, overlaying whatever programs or data are there.
Main memory utilization is extremely inefficient. Any program, no matter how small, occupies
an entire partition. In our example, there may be a program whose length is less than 2 Mbytes;
yet it occupies an 8-Mbyte partition whenever it is swapped in. This phenomenon, in which there
is wasted space internal to a partition due to the fact that the block of data loaded is smaller than
the partition, is referred to as internal fragmentation.
Both of these problems can be lessened, though not solved, by using unequal size partitions (in
the figure below). In this example, programs as large as 16 Mbytes can be accommodated
without overlays. Partitions smaller than 8 Mbytes allow smaller programs to be accommodated
with less internal fragmentation.
The use of fixed partitioning is almost unknown today. One example of a successful operating
system that did use this technique was an early IBM mainframe operating system, OS/MFT
(Multiprogramming with a Fixed Number of Tasks).
Dynamic Partitioning
To overcome some of the difficulties with fixed partitioning, an approach known as dynamic
partitioning was developed. Again, this approach has been supplanted by more sophisticated
memory management techniques. An important operating system that used this technique was
IBM’s mainframe operating system, OS/MVT (Multiprogramming with a Variable Number of
Tasks).
With dynamic partitioning, the partitions are of variable length and number. When a process is
brought into main memory, it is allocated exactly as much memory as it requires and no more.
An example, using 64 Mbytes of main memory, is shown in the figure below. Initially, main
memory is empty, except for the OS (a). The first three processes are loaded in, starting where
the operating system ends and occupying just enough space for each process (b, c, d). This leaves
a “hole” at
the end of memory that is too small for a fourth process. At some point, none of the processes in
memory is ready. The operating system swaps out process 2 (e), which leaves sufficient room to
load a new process, process 4 (f). Because process 4 is smaller than process 2, another small hole
is created. Later, a point is reached at which none of the processes in main memory is ready, but
process 2, in the Ready-Suspend state, is available. Because there is insufficient room in memory
for process 2, the operating system swaps process 1 out (g) and swaps process 2 back in (h).
As this example shows, this method starts out well, but eventually it leads to a situation in which
there are a lot of small holes in memory. As time goes on, memory becomes more and more
fragmented, and memory utilization declines. This phenomenon is referred to as external
fragmentation, indicating that the memory that is external to all partitions becomes increasingly
fragmented. This is in contrast to internal fragmentation, referred to earlier.
One technique for overcoming external fragmentation is compaction: From time to time, the OS
shifts the processes so that they are contiguous and so that all of the free memory is together in
one block. For example, in the figure below, compaction