Virtualization- Memoey Flashcards
Evolution of computer systems
the ease of building computer systems in the early days was attributed to users’ modest expectations. It is the evolving demands and expectations of users for improved functionality that have led to the complexities and challenges faced in modern computer system development. This shift in user expectations has necessitated ongoing innovation and development in the field of computer systems.
More hardware resource available.
over time, the physical components and resources used in computing systems have become more abundant, powerful, and accessible
Early systems
In the early days of computing, memory management was straightforward and lacked significant abstraction for users. The computer’s physical memory was divided into distinct regions. The operating system (OS), essentially a collection of routines or a library, resided in memory, typically starting at address 0. In addition to the OS, there was just one running program or process that occupied physical memory, usually beginning at address 64k, and it utilized the remaining memory.
This memory layout offered users little in the way of abstractions or illusions, and user expectations from the OS were minimal. For early OS developers, life was relatively uncomplicated due to the simplicity of memory management in these systems.
“abstraction” refers to the concept of hiding complex details and providing a simplified interface or representation for users or programmers.
Multiprogramming
The era of multiprogramming emerged as a response to the high cost of early computer machines. During this period, multiple processes were made ready to run on a computer simultaneously, and the operating system would switch between them. This context switching often occurred when one of the processes needed to perform input/output (I/O) operations. This approach aimed to enhance the effective utilization of the CPU by keeping it busy with tasks as much as possible.
The adoption of multiprogramming was crucial for maximizing the efficiency and productivity of these costly machines.
context switching I/O further explained.
When a process initiates an I/O operation, it often involves waiting for external resources (e.g., a file to load, a user to type something, data to arrive over the network). During this waiting period, the CPU could remain idle, which is inefficient.
To make better use of the CPU’s time, the operating system employs context switching. It temporarily pauses the process that’s waiting for the I/O operation to complete and switches to another process that is ready to run. This way, the CPU stays active and can work on other tasks while waiting for the I/O operation to finish.
Time Sharing
Initially, batch computing had limitations, including long program-debug cycles, which led to the demand for more interactive and concurrent usage of machines.
To implement time sharing, one approach was to run one process for a brief period, granting it full access to all memory, and then saving its state, including physical memory contents, to disk before loading another process. However, this approach was deemed slow and inefficient, especially as memory capacities grew, due to the time-consuming process of saving and restoring memory contents to disk.
An alternative and more efficient approach emerged, involving leaving processes in memory while switching between them, allowing the operating system to implement time sharing more effectively. However, this approach introduced a new challenge: the need to ensure that one process couldn’t access or interfere with another’s memory, which is where the concept of memory protection became crucial.
As time sharing gained popularity, it placed new demands on the operating system, emphasizing the importance of memory protection. The operating system had to create a reliable and easy-to-use abstraction of physical memory to ensure that processes remained isolated and secure from one another, thereby enabling multiple programs to coexist concurrently in memory.
Address space
crucial for understanding how memory is managed and virtualized. The address space is an abstraction provided by the operating system to the running program, representing the program’s view of memory within the system.
Components: The address space of a process encompasses all the memory state required by the running program. It includes:
Code: The program’s instructions are stored in memory, typically at the top of the address space.
Stack: The stack is used to manage the program’s function call chain, allocate local variables, and pass parameters and return values between functions.
Heap: The heap is used for dynamically-allocated, user-managed memory..
Generating Logical Address Space:
the process of defining how a program’s memory is organized and accessed when it runs. This process involves specifying the memory addresses where different components of the program will reside. The “logical” aspect means that these addresses are abstract and not tied to physical memory locations. Instead, they provide a consistent and virtual representation of memory for the program.
Compiler: When a program is written in a high-level programming language, it needs to be translated into machine code that the computer can execute. This translation is performed by a compiler. During this process, the compiler generates an object code file that contains information about the program’s instructions (code) and data.
Linker: Programs are often divided into multiple source code files. The linker takes these object code files generated by the compiler and combines them into a single executable file. This file not only contains the program’s machine instructions but also specifies how the program should be loaded into memory. It defines the layout of the program’s logical address space.
Loader: When you run an executable program, the loader in the operating system reads the instructions within the executable file. It sets up the logical address space based on the information provided by the linker. This involves allocating memory for the program’s code, data, stack, and heap according to the specified layout.
In essence, generating the logical address space is about defining the memory structure that the program will perceive when it runs. This abstraction allows the program to interact with memory in a consistent and organized manner, regardless of the actual physical locations of memory used by the computer.
Address space structure
The address space of a process typically has a consistent structure across all processes, regardless of their specific functionality. This structure includes the following components:
Program Code: This section contains the program’s instructions (code). It is usually located at the beginning of the address space.
Stack: The stack is used to manage local variables, function arguments, and return values. It grows and shrinks as functions are called and return.
Heap: The heap is reserved for dynamically allocated memory, user-managed memory,
Data: This section contains global variables and data used by the program. It may also include statically-initialized variables.
HOW TO ALLOCATE AND MANAGE MEMORY
In UNIX/C programs: types of memory allocation
Stack Memory (Automatic Memory):. Stack memory is suitable for short-lived data with automatic management.
Stack memory is managed implicitly by the compiler for you, the programmer.
It is typically used for short-lived data, such as local variables within a function.
Memory allocation on the stack is straightforward and automatic. You declare variables, and the compiler takes care of allocating memory when the function is called and deallocating it when the function returns.
However, stack memory is limited in size and scope. Any data allocated on the stack is only accessible within the function where it’s declared, and it gets automatically deallocated when the function exits.
Example in C:
void func() {
int x; // declares an integer on the stack
// …
Heap Memory: heap memory is used for more dynamic and long-lived data that you manage explicitly
Heap memory is explicitly managed by you, the programmer. You have control over memory allocation and deallocation.
It is typically used for dynamic and long-lived data, such as data structures that need to persist beyond the scope of a single function.
Memory allocation on the heap involves using functions like malloc() (memory allocation) and free() (memory deallocation).
While heap memory provides more flexibility and allows data to be shared across functions and scopes, it also requires careful management to avoid memory leaks and other issues.
Example in C:
void func() {
int *x = (int *) malloc(sizeof(int)); // allocates an integer on the heap
// …
14.2 The malloc() Call
you pass it a size asking for some
room on the heap, and it either succeeds and gives you back a pointer to
the newly-allocated space, or fails and returns NULL.
Malloc Function: malloc() is a standard C library function used for dynamic memory allocation. It allocates a specified amount of memory on the heap and returns a pointer to the first byte of the allocated memory.
Including the Header File: To use malloc(), you need to include the <stdlib.h> header file. This header file provides the necessary declarations and definitions for memory allocation functions.</stdlib.h>
Memory Size Calculation: When calling malloc(), you specify the size of the memory block you want to allocate in bytes. You typically use the sizeof operator to calculate the size based on the type of data you want to store. For example, sizeof(double) calculates the size of a double in bytes.
Type Casting: The return value of malloc() is a pointer to void (void). To use this memory as a specific data type, you can cast the pointer to the desired type. For example, if you want to allocate memory for a double, you cast the result to (double).
Checking for Allocation Failure: malloc() may fail to allocate memory if there is not enough free memory available. In such cases, it returns a NULL pointer. It’s essential to check the return value to ensure that the allocation was successful before using the allocated memory.
The free() Call
To free
heap memory that is no longer in use, or that was that was previously allocated on the heap.
Usage: free() is called with a single argument, which is a pointer to the memory block that you want to deallocate. This pointer should have been previously returned by a memory allocation function like malloc().
Responsibility: It’s the programmer’s responsibility to ensure that memory allocated on the heap is eventually freed when it’s no longer needed. Failure to do so can result in memory leaks, where allocated memory is not released and becomes unavailable for further use.
No Need to Specify Size: Unlike memory allocation functions like malloc(), you don’t need to specify the size when calling free(). The memory allocation library keeps track of the size of the allocated memory block internally.
Underlying OS Support
malloc() and free() are not themselves system calls. Instead, they are implemented as library functions provided by the C standard library. serve as a user-friendly interface for managing memory within a program, and they rely on underlying system calls to perform the actual memory allocation and deallocation tasks with the operating system.
In virtualization of CPU: limited direct execution
limited direct execution (LDE) is a strategy used in virtualization and operating system design to balance efficiency and control. It allows user-level programs to run directly on the hardware but ensures that the operating system intervenes at critical moments, such as system calls and timer interrupts, to maintain control and deliver an efficient virtualization experience. This approach is crucial for achieving the key goals of modern operating systems.
In virtualizing memory
How can we build an efficient and
flexible virtualization of memory?
* How do we maintain control over which memory locations an
application can access, and thus ensure that application memory accesses are properly restricted?
address translation
Address translation is a hardware process that converts virtual memory addresses used by programs into physical memory addresses where data is stored. This helps keep programs separate, ensures data is in the right place, and is done for every memory access. In simple terms, it helps programs find the actual location of their data in memory.
Memory Isolation: Address translation allows multiple processes or programs to run concurrently in a shared memory environment without interfering with each other. Each process sees its own virtual address space, which is translated to a unique physical address space by the hardware. This ensures that one program cannot directly access the memory of another, enhancing security and stability.
Flexibility: Address translation enables the operating system to load programs and data into physical memory
Hardware and Low-Level Mechanisms: The hardware alone can efficiently perform memory address translation between virtual and physical memory. It provides the foundation for memory virtualization.
OS Management: The operating system must step in at critical moments to configure the hardware for correct address translations. It is responsible for managing memory, keeping track of available and used memory locations, and ensuring proper control over memory usage.
Creating the Illusion: The ultimate goal of these efforts is to create a “beautiful illusion” for programs. This illusion makes each program believe it has its own private memory space, where its code and data exclusively reside. This abstraction simplifies programming and improves system security.
Reality Behind the Illusion: In reality, multiple programs often share physical memory, with the CPU switching between them. The OS, with the help of hardware virtualization, transforms this complex reality into a user-friendly and powerful abstraction.
In essence, the OS, working in tandem with hardware, transforms the intricate hardware memory-sharing reality into a simple and efficient virtual memory system, making it easier for programs to run and ensuring they don’t interfere with each other. This abstraction enhances the usability and security of computer systems.
Physical Memory with a Single Relocated Process
the key idea here is that the operating system can move a program’s address space to different physical memory locations for efficient memory management. However, it does so in a way that maintains the illusion for the program that its virtual address space still starts at 0. This is achieved through address translation and hardware support, ensuring the program’s correct functioning despite the underlying physical memory layout changes.
Program’s Perspective:
From the program’s point of view, it believes that its memory starts at address 0 and can extend up to 16 KB (kilobytes). This is the virtual address space that the program sees and operates within.
OS Objective:
The operating system (OS) wants to place the program’s address space in physical memory in a way that is efficient for memory management. However, it needs to maintain the illusion for the program that its address space still starts at 0.
Physical Memory Layout (Illustration):
The illustration in the passage shows how physical memory is organized after this relocation. Here’s what it represents:
The OS reserves the first slot of physical memory for itself. In the example, this area spans from 0 KB to 16 KB.
The OS has relocated the program’s address space to start at a different physical memory address, specifically at 32 KB. This means that in the actual physical memory, the program’s data and code reside in the memory range from 32 KB to 48 KB.
The remaining memory slots are indicated as free, in this case, memory ranges from 16 KB to 32 KB and from 48 KB to 64 KB.
The Illusion of Virtual Address Space:
Despite the physical relocation, the program is unaware of this change. It still operates as if its address space begins at 0. When the program accesses memory at address 0 in its code, the hardware and OS ensure that the access is directed to the correct physical memory location, which, in this case, is 32 KB