In this extra optional problem, you can implement the code for the "base and bound" design. We can't fiddle with the actual virtual memory system, so instead we've designed a simulation program that enacts how virtual memory is implemented in a simulated setting.
Specifically, we've provided a class VirtualMemory that represents a program's virtual address space, managed using the "base and bound" design. You create one by specifying how big the virtual memory space should be
// Initialize our virtual address space with 1000 bytes
VirtualMemory v_mem(1000);
VirtualMemory has base and bound instance variables to store the base and bound for the memory region:
char *base;
size_t bound;
How do we get a pointer to this fake virtual address space? You can get a pointer to the start like this:
/* Get a virtual pointer to the start of the memory space.
* A VirtualPointer can be used just like a
* regular char * pointer.
*/
VirtualPointer ptr = v_mem.get_start_ptr();
A VirtualPointer is a simulated pointer type that we've defined that hooks into VirtualMemory. We need the ability to intercept every memory access, and we can't easily do that with a real program's pointers unless we're the OS :) So instead, we defined a custom type VirtualPointer set up so that every time someone dereferences one, it will call a function translate that we will write the code for. A VirtualPointer behaves just like a regular char * pointer, though - we have implemented the functionality to allow you to dereference it (which calls translate), do pointer arithmetic with it, and print it out:
*ptr = 'h';
*(ptr + 5) = 'z';
cout << ptr + 5 << endl;
When we print it, it prints a hex number that is the offset. E.g. the above would print:
0x5
Your task is to implement the translate method within VirtualMemory; it takes in a VirtualPointer (a virtual address) and should use the base and bound to translate it to a physical address (represented as a char *) and return that. If the virtual address's offset is invalid (i.e. outside segment bounds) you should print an error message, then cause a segmentation fault like this: raise(SIGSEGV), and then return nullptr (that line will never be reached, but otherwise C++ will complain about no return value).
char *translate(const VirtualPointer& p);
Remember the steps for base and bound:
- Compare offset to the bound, error if >=
- Otherwise, add the base to virtual address offset to produce physical address
You can get the offset of a VirtualPointer by doing p.offset.
Task: implement the translate method and test it by running the provided test program, which attempts to access 3 addresses, the first two of which are valid, and the third of which is invalid and should cause a crash.
int main(int argc, char *argv[]) {
VirtualMemory v_mem(kMemorySize);
VirtualPointer ptr = v_mem.get_start_ptr();
*ptr = 'h';
cout << "Data at virtual address " << ptr << " is: " << *ptr << endl;
VirtualPointer newPtr = ptr + (kMemorySize - 1);
*newPtr = 'e';
cout << "Data at virtual address " << newPtr << " is: " << *newPtr << endl;
VirtualPointer badPtr = ptr + kMemorySize * 2;
cout << "Trying to write to virtual address " << badPtr << endl;
*badPtr = 'l';
return 0;
}
It should output the following: (the "memory violation" line is the error message printed by translate. The "Segmentation fault" line is printed automatically by the shell after this program receives a segmentation fault).
Data at virtual address 0x0 is: h
Data at virtual address 0x3e7 is: e
Trying to write to virtual address 0x7d0
memory violation - accessing invalid offset 2000
Segmentation fault
NOTE: in a real virtual memory system, only the OS can see physical addresses; a user program is only aware of virtual addresses. You might imagine that test.cc is a user program, and translate is in the OS.