CS361: Operating System

Jian Huang — Spring 2012

EECS | University of Tennessee - Knoxville

Address and Memory Space

Memory is central to the operation of a modern computer. Often it is considered as a large array of bytes, each with its own address. Memory access happens at a great frequency, especially due to the load-store architecture. Memory access takes place as a transaction on the memory bus, typically requires a CPU stall because completing a memory access may take many cycles. Thereby creating the need of having cache between CPU and memory to accormodate the speed differentials.

Interestingly, and very importantly, speed and performance issues are not the top concern in an operating system. Instead, it is the sharing of memory space and thereby the protection mechanism that needs to be in place. In concept, we need to make sure that each process has a separate memory space, and need the ability to determine the range of legal address that a process may access. Key to this process, we need to understand the following concepts:

  1. symbolic address
  2. logical address
  3. physical address

Symbolic addresses: addresses in the source program (including assembly) are generally symbolic.

Logical addresses: when executing a program, CPU speaks the language of logical address and logical address only.

Physical addresses: the actual/physical bytes on the memory banks are indexed by physical addresses.

Binding is a fancy way of saying mapping from one address space to another. In the context of operating system, address binding takes on a special meaning - the mapping between symbolic addresses and logical addresses. Please refer to Fig. 8.3 in the text book. In short, address binding can take place at compile time, link/load time as well as execution time. Please be clear about each of these scenarios.

Regardless of when binding takes place, eventually CPU will need to access memory - the address generated by CPU is called "logical address", aka "virtual address". After address binding, each process logically has its complete address space. On a 32-bit system, that amounts to 2GB. These addresses are also called virtual address, in many cases, used interchangeably with logical address. The CPU "hardware" computes every address generated in user mode, not kernel mode.

The mapping logical address to physical address is handled by the hardware device of MMU, memory management unit. Until execution time, physical address has no real meaning for a program. There are many ways to implement MMU, the simplest is to treat base register as a "relocation" register - adding relocation register to the virtual address to get physical address. Relocatable address and absolution address are two terms used during the process of address binding. The main difference is that relocatable address is typically represented as xx bytes from the beginning of a module/or other point of reference. The job of converting relocatable address is handled by loader(ld), except for when dynamic linking libraries are used.

Memory banks respond to "memory-address register" - the content of that register is called "physical address".

Dynamic Linking

Not all OS support dynamically linked libraries, but all modtern operating systems do. The most common is static linking - where system libraries are treated just like another object module and are combined by loader into the binary program image.

Dynamic linking postpones linking until execution time - without this each program must include a copy of its language library in the executable image. Dynamic linking includes a stub in the image for each library reference. When the stub is executed, it checks to see whether the needed routine is already in memory; if not - loads the routine into memory. Try:

man dlopen
man dlsym

The following is an example code that explicitly uses and manages dynamic libraries.

int main(int argc, char **argv) 
{
   void *lib_handle;
   int (*marching_cube)(void *);
   char *error;
   unsigned char *volume;

   lib_handle = dlopen("/opt/lib/libvcb.so", RTLD_NOW);
   if (!lib_handle) 
   {
      fprintf(stderr, "%s\n", dlerror());
      exit(1);
   }

   marching_cube = dlsym(lib_handle, "vcb_mcube");
   if ((error = dlerror()) != NULL)  
   {
      fprintf(stderr, "%s\n", error);
      exit(1);
   }

   (*marching_cube)(volume);

   dlclose(lib_handle);
   return 0;
}


Jian Huang / EECS /UTK / revised 01/2012