Virtual Journey into the machine: the Memory
This is a really complex and interesting topic. I love those 2 resources which do a great job at explaining how processes are mapped in a Virtual Memory:
- This blog has 2 posts about that topic: How the Kernel Manages Your Memory and Anatomy of a program in Memory.
- Chapter 9 of Computer Systems: a Programmer’s Perspective.
The following is a way to express what I understood so far.
Virtual Memory seen as Theatre decors
I enjoy making links between arts and computer science. I see the way the kernel handles the memory (RAM) as a theatre stage. It creates a uniform, very organized way to show very different scenes with important size and efficiency constraints.
The decors can either be pulled down, they are in mapped in memory, ready to be pulled down and they are attached to some machinery ready to go, or tucked away on disk (rolled up in the basement).
It is easy on a theatre stage to switch from a kitchen to an exterior scene, similar to context switching, where the CPU switches processes. All the decors are basically the same thing, a sheet of painted paper that goes down, and painted wood on the side. Several decors can share some elements. The kernel makes a similar representation of programs’ memory, they are all represented the same way (see below and /proc post), this is the virtual memory, and there are also shared memory segments, like shared libraries.
As the play goes, a scene (the CPU) may require to make rain (get value at address
a). This is translated into orders a machine or humans can follow like “add a painted cloud” and someone must fetch it. Similarly in the machine the memory management unit must translate
a, it has to figure out its actual physical location and the value must be returned.
Generic Overview of the Virtual Memory
The Virtual Memory can be used for caching, memory management and memory protection.
The following illustrates caching:
In order to deal with memory, there is the concept of a page: that is the unit of transfer.
The kernel sees an image of a process memory: the virtual memory. With this concept, all processes show a uniform interface to interact with. When the CPU requires a value to be loaded, the memory management unit(MMU) needs to look for the right page in which this value resides. It disposes of page tables that match every virtual page with its real counterpart. It can be:
- a physical page cached in RAM (option A). In that case, the MMU is then able to reconstruct the address of the variable, and the memory can return the variable.
- a page that is not cached in memory(option B). In that case, a page fault is raised, a victim page in RAM is selected, the page holding the variable on disk is loaded in memory instead, and the page table is updated. The system is back to option A.
- unallocated (option C). If it is not allocated, then a call to
mallocwill mark it as allocated, return a value, but it will not be mapped with actual physical memory (see this post), that will only be done when the process actively reads/writes at that position.
An example of memory management:
Multiple processes may call the same pages, such as common libraries. In that case, to be more efficient, the page is loaded once in memory, as shared memory and the processes can map this single instance in their page table.
Virtual Memory in Linux
Linux decided to represent the virtual memory of each process in the form represented above. It creates different areas or segments. Addresses increase from bottom to top. An area is defined as as a continuous chunck of allocated virtual memory whose pages are related in some way (Computer Systems: A Programmer’s Perspective).
This representation lies in the
mm_struct of the
task_struct the kernel maintains for each process.
For each running process, this is written in the file /proc/[pid]/maps (see this post for an example).
To illustrate the above, I modified the infinite_loop code from the post linked above to display the address of each variable:
char *s1 = "Hello World";
s2 = strdup(s1);
printf("Address of a variable on the stack: %p\n", (void *) &s1);
printf("Address of a string: %p\n", (void *) s1);
printf("Address of a variable on the heap: %p\n", (void *) s2);
Address of a variable on the stack: 0x7ffd3bef56e0
Address of a string: 0x400678
Address of a variable on the heap: 0x2153010
The string “Hello World” is actually located in the text segment, located below the heap, itself below the stack.