presentation source

Download Report

Transcript presentation source

Chapter 10 - Memory
Management
– Memory management can have a large
influence on the performance of a program.
– Operating system handles allocation of memory
to processes.
– Processes typically manage the OS-supplied
memory (e.g., new or malloc()) - Figure 10.1.
– Helpful to study the layout of memory within a
process before discussing OS memory
functions.
• Linking & Loading a Process
– Various steps are required to take the source code of a
program and make it runnable (Figure 10.2).
– Compiler produces object modules (in UNIX: “.o”
files; in DOS/WIN: “.obj” files).
– Object module formats are varied; Figures 10.3, 10.4
& 10.4 demonstrate a typical format containing:
• Header - directory of the rest of the file contents.
• Machine code - compiler-generated machine code; the code
references in here are relative.
• Initialized data - globals that have compile-time values.
• Uninitialized data - globals that do not have compile-time
values (and thus no space allocated in object file).
• Linking & Loading a Process
• Symbol table - defined external symbols (code/data defined
in this object file that can be called from elsewhere) and
undefined external symbols (referenced in this module, but
found elsewhere).
• Relocation information - information about the object file
that permits the linker to connect object files into a
coherent, executable unit (aka load module).
– UNIX notes:
• Many UNIX object files are in either COFF (Common
Object File Format) or ELF (Executable and Linking
Format).
• Unix file command can tell you something about the object
file and/or executable.
• Unix ld command is used to combine object files into an
executable; it is sometimes called implicitly.
• Linking & Loading a Process
– More UNIX notes:
• Unix nm command will print part of the symbol table of an
object file and/or executable binary.
• UNIX ar command used to manage libraries of object files
(“.a” files on UNIX; “.lib” files on DOS/WIN).
• For more information, see the following man pages: ld, nm,
a.out, ar, strip & elf.
– The linker is responsible for combining one or more
object files along with zero or more libraries into a
load module (executable binary).
– Linker steps are quite involved (page 382-383) but
boil down to two steps: relocation and linking.
• Linking & Loading a Process
– Relocation - The correction of addresses within the
object modules relative to the linker’s placement of
other object modules within the binary (Figure 10.6).
– Relocation can be static (done once by the linker at
link time) or dynamic (a base register is added to the
address in the binary continually at run time).
– Relocation is also called binding.
– Linking - Modification of addresses where one object
module references code/data in another object
module (also called resolution of unsatisfied external
references). Figure 10.7.
– Libraries are used to store common functions
(accessed via “-lm” on cc/ld = /usr/lib/libm.a).
• Loading a Binary for Execution
– The load module (executable binary) is loaded into
the process’ memory area using OS-specific memory
layouts (such as Figure 10.8).
– Note how some areas are not stored in the binary but
have to be created for execution in memory
(uninitialized & stack data).
– OS calculates the memory required for the particular
binary, including a default stack size. The UNIX size
command will show you the expected memory
“footprint” from a binary (try “size a.out”).
– Notice how the memory areas of a process are laid
out to permit dynamic growth (Figure 10.9) via any
future new or malloc() calls.
• Variations in Program Loading
– Larger programs result in large object files and
libraries. The resulting binary (load module) can be
huge.
– One technique to cut down the size is load time
dynamic linking - delay the linking in of library
routines at process creation time instead of at binary
creation time. The resulting process image in
memory will have all the externals satisfied (compare
Figure 10.10 with 10.11).
– Another technique is run time dynamic linking rather than deferring linking at binary load time you
delay it until the last possible moment -- at the time
of reference by the program (Figure 10.12).
• Variations in Program Loading
– Figure 10.13 summarizes the three linking methods
(static, load time dynamic, run time dynamic).
– Dynamic linking is also called late binding.
– Interesting comparison of the costs involved with the
three methods on page 393. This is an example of the
classic time/space tradeoff. Decreasing space
requirements will usually increase time requirements
and vice versa.
– Book doesn’t mention a forth very popular type of
late binding -- use of shared libraries.
– With the three techniques above each process ends up
requiring memory space allocated for all of the object
modules the program uses.
• Variations in Program Loading - Shared Libraries
– Rather than having each process load up it’s own
private copy of common library routines you can
keep only one copy of a common routine in memory
and link each process to a block of shared memory
containing the common routine.
– For instance, rather than 100 processes each loading
up the object module for the printf() routine you have
each one call a single copy of printf().
– Thus, the linking happens at runtime and rather than
copying in the code from a common library the
executable is routed to the shared library routine.
• Variations in Program Loading - Shared Libraries
– The shared library routine must be written such that it
does not use any private global data of any one
particular process, else you couldn’t have more than
one process sharing the code.
– This is called reentrant, pure or PIC (Position
Independent Code) code. From the “CC” man page:
-pic Produces position-independent code. Use this option to
compile source files when building a shared library. Each
reference to a global datum is generated as a dereference of a
pointer in the global offset table. Each function call is generated
in pc-relative addressing mode through a procedure linkage table.
• Variations in Program Loading - Shared Libraries
– Shared library code resides in special “.so” files. For
example, “ls -l /lib/libc.*” on xi shows:
-rw-r--r-1 bin
lrwxrwxrwx 1 root
-rwxr-xr-x 1 bin
bin
root
bin
1153120 Dec 14 1996 /lib/libc.a
11 Aug 7 1996 /lib/libc.so -> ./libc.so.1
663460 Dec 14 1996 /lib/libc.so.1
– libc.a contains the statically-linked object modules.
– libc.so.1 contains the shared library object modules
that are linked dynamically at runtime to a single
copy of the routines in memory shared between all
processes.
– Result is decrease in overall memory usage.
– Shared lib support requires OS intervention!
• Variations in Program Loading - Shared Libraries
– Shared libraries are named by version numbers, so
you can be sure a program compiled against a
particular version of a shared library will run with the
correct version (if it is installed).
– The ldd command will show you what shared
libraries a particular binary expects to be available.
– The UNIX environment variable
LD_LIBRARY_PATH is used to indicate where the
runtime linker can find the “.a” and “.so” files.
– DLLs under Windows-based operating system serve a
similar function (Dynamically Linked Library).
Windows uses the PATH variable to find DLLs.
– Skip 10.5, 10.6, 10.8, 10.9, 10.10
• Section 10.7: Dynamic Memory Allocation
– Static allocation of memory within an operating
system is not a good idea, since processes are
dynamic within their own behavior and in their life
cycles.
– OS has to allocate blocks of memory depending on
demand. OS has to figure out how to:
• Keep track of blocks in use and free.
• Allocate blocks when a request comes in.
– Process memory patterns can lead to memory
fragmentation as different sized blocks are allocated
and released (Figure 10.19).
• Logical & Physical Memory
– Review: physical addresses on the CRA-1 are used
while in system mode so the processor has access to
all of memory.
– When in user mode, the processor is limited by the
value of base and limit. This is called logical
addressing.
– The hardware and operating system create multiple
logical address spaces within the single physical
address space (Figure 10.27).
– At this point we are still considering the logical
address space to be contiguous within physical
memory.
• Allocating Contiguous Memory to Processes
– SOS & JavaSOS divide memory into even-sized
chunks (static allocation).
– Not a very flexible situation if you have processes
dynamically changing their size and number over
time (Figure 10.28).
– The next step would be to dynamically assign
memory as processes change in size and enter/exit the
system. This is a Difficult Problem (we skipped this
in section 10.7).
– Only makes sense to bother with dynamic memory
allocation if it is desirable to share the machine
between multiple processes (multiprogramming).
• Allocating Contiguous Memory to Processes
– OS & Hardware must provide:
• Memory allocation scheme - various algorithms (again
which we skipped) mentioned in earlier sections.
• Memory protection scheme - can use ye olde base & bound
registers (requires contiguous memory allocation) or keyed
memory (permits non-contiguous memory allocation) or
as-yet not discussed techniques (Figure 10.29).
• Memory Management System Calls
– A process that does dynamic memory programming
requires OS services to adjust it’s memory
boundaries.
– One simple SOS solution would be to add yet another
system call for memory requests (Figure 10.30).
• Memory Management System Calls
– UNIX uses the brk() call (named so as not to conflict
with the C reserved word “break”) to extend the
upper bound of the process:
int brk (char *addr);
// 0 == worked, -1 == failed
– Execution of the brk() call results in the extension of
the dynamic data section of the process memory map
(Figure 10.31).
– Notice the unused logical address space -- this is
memory addresses that are not mapped to physical
memory. This requires a non-contiguous memory
allocation scheme within the OS to support.
– Usually, new & malloc() indirectly result in brk()
calls, depending on the size of the requests.
• Memory Management System Calls
– An internal process memory manager (typically part
of the runtime support in a language) takes care of
intra-process memory requests.
– The internal process memory manager calls the
operating system only if the processes memory limit
isn’t large enough to satisfy the program’s needs.
– The two levels of memory management result in most
of the malloc()/new/free()/delete operations being
handled within the process.
– Note that the behavior of most programs means that
their memory demands increase over time.
– Figure 10.32 & 10.33 show these two levels of
memory management at work.
• Memory Management System Calls
– The semantics of the brk() system call come from an
era where the mapping of logical addresses was in a
contiguous physical address space (since brk() grows
the process from one of the ends and not in the
middle).
– A proposed SOS call acknowledges that modern
memory managers can use non-contiguous schemes
(such as paging, presented in the next chapter):
char *AllocateMemory(int length);
– Notice how it looks a lot like malloc().
– Skip section 10.16.