Why The Virtual Memory (VM)
History in short
In 1979 was implemented the 3BSD by researchers at Berkeley. That was the first Unix release with virtual memory.
In 1992 the virtual memory introduction turn was for Microsoft and its Windows NT.
In 2002 Apple began the implementation of virtual memory when moving from OS 9 to OS X operating system for MAC.
This information tells us about virtual memory as an important idea. The question is why?
The main problem that conducts to the Virtual Memory (VM), was the resource optimization for the time-sharing processing to support multitasking and multi-user operating systems.
The idea is creating logical spaces of memory and backing it with real physical memory coordinated according to a set of mapping tables created and maintained dynamically.
Specifically, the reasons are:
- The OS processes only can see a dedicated logical space of memory each.
- An operating system process cannot interfere with others.
- The sparse memory layout is handled efficiently.
- Processes can share programs and data.
- Efficiency can be gained by creating a memory hierarchy.
The context
If you need to compute two simple values by addition you can easily store both values in your brain and calculate de result that is put into your brain too but, if you need to compute complex values you will need to store that values in an external media. And, it can be typically paper. Then, you do different simple calculus partially written on the paper and as an outcome. Therefore, we can get the final result. Here we can see two important things.
- When data is simple, the process is simple and the storage is simple
- When data is complex, the process is complex and the storage is complex
This is the point: A computer has to solve these kinds of problems too and face their complexity. In memory case, the answer is Virtual Memory (VM).
What keeps in mind about storage and memory?
Computer data storage is a technology consisting of computer components and recording media that are used to retain digital data.
The storage hierarchy lets the central processing unit (CPU) performs computations with data. Usually, the storage hierarchy is organized in levels according to its cost and performance puts fast and expensive hardware close to the CPU and less expensive but slower hardware further away. Generally, the faster are volatile and known as “memory” and the slower are persistent and are known as “storage”
Above we have three levels of computer data storage. Let’s see them:
Primary storage
It is the level the CPU directly access to compute and it is known as main memory or simply as memory. The primary storage has processor registers that are ultra-fasts, processor cache as speed up element between ultra-fast processor registers, and main memory. The RAM memory is implanted on integrated circuit technology. This technology allows semiconductor memory an acceptable trade-off and it is economically competitive
Secondary storage
The main difference with primary memory is the secondary is not accessible directly by the CPU. Thus the system buses are used to move data from secondary storage to the primary. The clue here is the secondary memory is nonvolatile. The most common devices in this kind of storage are the hard disk drives (HDD) and the solid-state drives (SDD).
Tertiary storage
This memory level is for archiving information that is rarely accessed. It is common that these devices use robotic mechanisms to mount or dismount the mass storage media. The most common devices to this category are tape or optic-disk libraries.
Off-line storage
When a storage device is not under the control of the CPU we can say it is off-line storage. These are secondary or tertiary tier memory devices and are removable or pluggable.
Virtual Memory at MMU
The memory management unit (MMU), is a small device located between CPU and RAM having all memory references passed through itself and get the actual memory address. Therefore, it performs the translation of virtual memory addresses to physical addresses. This is a hardware solution for Virtual Memory (VM) and uses the translation lookaside buffer (TLB) to do it.
Virtual Memory and HSA
A Heterogeneous System Architecture (HSA) is a set of specifications that integrates the central processing units (CPU), and graphics processors (GPU) on the same bus, with shared memory and tasks. This is a cross-vendor solution and, it is supported by AMD and ARM among many others. System-on-chip devices such as tablets, smartphones, other mobile devices, and video game consoles are implanted widely with heterogeneous computing (HSA).
System with HAS
System with HSA
Virtual Memory as a wider concept
Previous we talked about VM in hardware but the concept can be implanted in software too just as part of the Operating system consequently, the VM can expand its capacity further the memory boundaries.
And, this wider approach allows the operating system to addresses:
Easy managing
The physical space must be shared, and it might be shared in some very messy ways. However, the physical memory is not seen by the process, only the virtual memory. The logical view is typically a linear array of byte numbers 0, 1, 2, etc., to the maximum location number.
Let’s see:
vagrant@gogomillan:~$ rev & [1] 27363 vagrant@gogomillan:~/try$ ps PID TTY TIME CMD 22711 pts/2 00:00:04 bash 27363 pts/2 00:00:00 rev 27364 pts/2 00:00:00 ps [1]+ Stopped rev vagrant@gogomillan:~$
---
vagrant@gogomillan:~$ cat /proc/27363/maps 00400000-00402000 r-xp 00000000 08:01 55383 /usr/bin/rev 00601000-00602000 r--p 00001000 08:01 55383 /usr/bin/rev 00602000-00603000 rw-p 00002000 08:01 55383 /usr/bin/rev 0220e000-0222f000 rw-p 00000000 00:00 0 [heap] 7fbd149b4000-7fbd14b72000 r-xp 00000000 08:01 2077 /lib/x86_64-linux-gnu/libc-2.19.so ... 7fbd14fa0000-7fbd14fa1000 rw-p 00023000 08:01 2188 /lib/x86_64-linux-gnu/ld-2.19.so 7fbd14fa1000-7fbd14fa2000 rw-p 00000000 00:00 0 7ffecfa8a000-7ffecfaab000 rw-p 00000000 00:00 0 [stack] 7ffecfbdd000-7ffecfbdf000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] vagrant@gogomillan:~$ vagrant@gogomillan:~$ sudo ./pagemap 27363 0x0400000 Big endian? 0 Vaddr: 0x400000, Page_size: 4096, Entry_size: 8 Reading /proc/27363/pagemap at 0x2000 [0]0xf9 [1]0xf8 [2]0x3 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa6 Result: 0xa60000000003f8f9 PFN: 0x3f8f9 vagrant@gogomillan:~$
The previous example was running the “rev” command, the VM shows 0x0400000 as the memory address and the “Result” is 0x03F8F9 as the physical. Now let’s see with another:
vagrant@gogomillan:~$ cat - & [1] 27506 vagrant@gogomillan:~$ ps PID TTY TIME CMD 22711 pts/2 00:00:05 bash 27506 pts/2 00:00:00 cat 27507 pts/2 00:00:00 ps [1]+ Stopped cat - vagrant@gogomillan:~$ cat /proc/27506/maps 00400000-0040b000 r-xp 00000000 08:01 129 /bin/cat 0060a000-0060b000 r--p 0000a000 08:01 129 /bin/cat 0060b000-0060c000 rw-p 0000b000 08:01 129 /bin/cat 00a13000-00a34000 rw-p 00000000 00:00 0 [heap] 7fd76f8c0000-7fd76fa7e000 r-xp 00000000 08:01 2077 /lib/x86_64-linux-gnu/libc-2.19.so ... 7fd76feac000-7fd76fead000 rw-p 00023000 08:01 2188 /lib/x86_64-linux-gnu/ld-2.19.so 7fd76fead000-7fd76feae000 rw-p 00000000 00:00 0 7ffd9661a000-7ffd9663b000 rw-p 00000000 00:00 0 [stack] 7ffd966fd000-7ffd966ff000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] vagrant@gogomillan:~$ vagrant@gogomillan:~$ sudo ./pagemap 27506 0x0400000 Big endian? 0 Vaddr: 0x400000, Page_size: 4096, Entry_size: 8 Reading /proc/27506/pagemap at 0x2000 [0]0x9b [1]0xf8 [2]0x3 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa6 Result: 0xa60000000003f89b PFN: 0x3f89b vagrant@gogomillan:~$
The previous example was running the “rev” command, the VM shows 0x0400000 as the memory address and the “Result” is 0x03F89B as the physical. As we can see the VM is the same for both process hiding as a convenient fact the physical address.
To identify this data you need the privilege and access to the proc(5) file-system, particularly the maps and pagemap files.
Protected and secure
Unless the mapping tables have mapped the same physical memory into both processes’ virtual memory, the processes cannot affect each other’s memory. Even a crashing or badly misbehaving program cannot affect memory that is not mapped into its space
Let’s see:
vagrant@gogomillan:~$ cat - & [1] 27825 vagrant@gogomillan:~$ ps PID TTY TIME CMD 22711 pts/2 00:00:05 bash 27825 pts/2 00:00:00 cat 27826 pts/2 00:00:00 ps [1]+ Stopped cat - vagrant@gogomillan:~$ cat - & [2] 27828 vagrant@gogomillan:~$ ps PID TTY TIME CMD 22711 pts/2 00:00:05 bash 27825 pts/2 00:00:00 cat 27828 pts/2 00:00:00 cat 27829 pts/2 00:00:00 ps [2]+ Stopped cat -
--
vagrant@gogomillan:~$ cat /proc/27825/maps | head -1 00400000-0040b000 r-xp 00000000 08:01 129 /bin/cat vagrant@vagrant-ubuntu-trusty-64:~/try$ cat /proc/27828/maps | head -1 00400000-0040b000 r-xp 00000000 08:01 129 /bin/cat vagrant@gogomillan:~$ vagrant@gogomillan:~$ sudo ./pagemap 27825 0x0400000 Big endian? 0 Vaddr: 0x400000, Page_size: 4096, Entry_size: 8 Reading /proc/27825/pagemap at 0x2000 [0]0x9b [1]0xf8 [2]0x3 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa6 Result: 0xa60000000003f89b PFN: 0x3f89b vagrant@gogomillan:~$ sudo ./pagemap 27828 0x0400000 Big endian? 0 Vaddr: 0x400000, Page_size: 4096, Entry_size: 8 Reading /proc/27828/pagemap at 0x2000 [0]0x9b [1]0xf8 [2]0x3 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa6 Result: 0xa60000000003f89b PFN: 0x3f89b vagrant@gogomillan:~$
The above example shows us a “cat” command executed twice therefore we have two processes but the VM of both points to the same physical memory (0x03F89B). That’s because it is the same binary from the disc and the OS can save memory putting only one copy in memory and the processes have their pointers to the same memory page through the VM.
Optimizing limited resources
There are situations where it is convenient to layout memory usages including large gaps of unused memory. A good example is the gap between the top of the heap and the bottom of the stack is largely unused, the amount unused is not known until runtime as the stack and heap grow towards each other. By not mapping the memory in this gap, space does not consume physical memory, given that it doesn't need any physical memory, and that resource can be used elsewhere.
Let’s see:
vagrant@gogomillan:~$ cat /proc/27828/maps 00400000-0040b000 r-xp 00000000 08:01 129 /bin/cat 0060a000-0060b000 r--p 0000a000 08:01 129 /bin/cat 0060b000-0060c000 rw-p 0000b000 08:01 129 /bin/cat 0133c000-0135d000 rw-p 00000000 00:00 0 [heap] 7f592ebb5000-7f592ed73000 r-xp 00000000 08:01 2077 /lib/x86_64-linux-gnu/libc-2.19.so 7f592ed73000-7f592ef73000 ---p 001be000 08:01 2077 /lib/x86_64-linux-gnu/libc-2.19.so 7f592ef73000-7f592ef77000 r--p 001be000 08:01 2077 /lib/x86_64-linux-gnu/libc-2.19.so 7f592ef77000-7f592ef79000 rw-p 001c2000 08:01 2077 /lib/x86_64-linux-gnu/libc-2.19.so 7f592ef79000-7f592ef7e000 rw-p 00000000 00:00 0 7f592ef7e000-7f592efa1000 r-xp 00000000 08:01 2188 /lib/x86_64-linux-gnu/ld-2.19.so 7f592f00c000-7f592f195000 r--p 00000000 08:01 54941 /usr/lib/locale/locale-archive 7f592f195000-7f592f198000 rw-p 00000000 00:00 0 7f592f1a0000-7f592f1a1000 r--p 00022000 08:01 2188 /lib/x86_64-linux-gnu/ld-2.19.so 7f592f1a1000-7f592f1a2000 rw-p 00023000 08:01 2188 /lib/x86_64-linux-gnu/ld-2.19.so 7f592f1a2000-7f592f1a3000 rw-p 00000000 00:00 0 7ffcfafb5000-7ffcfafd6000 rw-p 00000000 00:00 0 [stack] 7ffcfafe6000-7ffcfafe8000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] vagrant@gogomillan:~$
The memory layout for the previous cat process has a big memory space between the heap and the stack with only a few segments used. Because these segments in the memory are continuous let the operating system optimize the resources.
Leveraging by synergy
By mapping the same physical memory into multiple processes, the memory is shared. This is particularly useful for code libraries, which can be put into physical memory only once, and then used by any process that needs the library by mapping the physical memory into the process' logical space.
Let’s see:
vagrant@gogomillan:~/try$ ps PID TTY TIME CMD 22711 pts/2 00:00:05 bash 27825 pts/2 00:00:00 cat 27973 pts/2 00:00:00 rev 27976 pts/2 00:00:00 ps vagrant@gogomillan:~/try$ cat /proc/27825/maps 00400000-0040b000 r-xp 00000000 08:01 129 /bin/cat 0060a000-0060b000 r--p 0000a000 08:01 129 /bin/cat 0060b000-0060c000 rw-p 0000b000 08:01 129 /bin/cat 00836000-00857000 rw-p 00000000 00:00 0 [heap] 7fda29af6000-7fda29cb4000 r-xp 00000000 08:01 2077 /lib/x86_64-linux-gnu/libc-2.19.so ... vagrant@gogomillan:~/try$ cat /proc/27973/maps 00400000-00402000 r-xp 00000000 08:01 55383 /usr/bin/rev 00601000-00602000 r--p 00001000 08:01 55383 /usr/bin/rev 00602000-00603000 rw-p 00002000 08:01 55383 /usr/bin/rev 00f42000-00f63000 rw-p 00000000 00:00 0 [heap] 7f3ec31ec000-7f3ec33aa000 r-xp 00000000 08:01 2077 /lib/x86_64-linux-gnu/libc-2.19.so ... vagrant@gogomillan:~/try$
--
vagrant@gogomillan:~/try$ sudo ./pagemap 27825 0x7fda29af6000 Big endian? 0 Vaddr: 0x7fda29af6000, Page_size: 4096, Entry_size: 8 Reading /proc/27825/pagemap at 0x3fed14d7b0 [0]0xd1 [1]0x1a [2]0x2 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa6 Result: 0xa600000000021ad1 PFN: 0x21ad1 vagrant@gogomillan:~/try$ sudo ./pagemap 27973 0x7f3ec31ec000 Big endian? 0 Vaddr: 0x7f3ec31ec000, Page_size: 4096, Entry_size: 8 Reading /proc/27973/pagemap at 0x3f9f618f60 [0]0xd1 [1]0x1a [2]0x2 [3]0x0 [4]0x0 [5]0x0 [6]0x0 [7]0xa6 Result: 0xa600000000021ad1 PFN: 0x21ad1 vagrant@gogomillan:~/try$
As we can see in the above example “cat” and “rev” both commands use the “libc-2.19.so” library. Accordingly, the Operating System create different VM addresses (0x7FDA29AF6000 and 0x7f3EC31EC000) but mapping to the same physical memory address (0x021AD1).
Gaining efficiency
The physical memory can be either RAM which is fast and expensive or disk which is slow but plentiful. Virtual locations that "go cold", that is, that contain code or data which is not now being used, are stored on disk. As the locations become "hot", the disk data is copied into RAM. This allows better use of resources and allows more programs to run well on a machine. The storage area on the disk can be a dedicated location called swap space. For code, the source file itself is used.
Let’s see:
Virtual memory and shared memory
Previously we see that multiple virtual memory spaces can be mapped to the same physical page. Among others, we can use this to communicate processes. This functionality is provided from operating system calls. One of these is shmat (Shared Memory Attach). The memory need not map to the same address. Shared memory can quickly fork child processes but marking writable pages as copy-on-write. The fork will no create a clone, but simply set up new page tables, copies of the original page tables. This way the child process starts quickly, and only copies what is needed so that both processes see the own image of the memory.
A good example is a database cache structure the Oracle Database Management System implants. Let’s see:
SQL> STARTUP ORACLE instance started. Total System Global Area 619360256 bytes Fixed Size 1338280 bytes Variable Size 377488472 bytes Database Buffers 234881024 bytes Redo Buffers 5652480 bytes Database mounted. Database opened.
The above sentence create shared Memory segments available to processes through the VM. Let’s continue:
vagrant@gogomez:~$ ipcs -a ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0xc616cc44 1056800768 oracle 660 619360256 0 0x0103f577 323158020 root 664 966 1 0x0000270f 325713925 root 666 1 2 ------ Semaphore Arrays -------- key semid owner perms nsems 0x0103eefd 0 root 664 1 0x0103eefe 32769 root 664 1 0x4b0d4514 1094844418 oracle 660 204 ------ Message Queues -------- key msqid owner perms used-bytes messages 0x000005a4 32768 root 644 0 0
We can see as the shared segments are addressable and are coordinated with semaphores.
The /proc data structure
We can see the documentation about proc(5) to identify that exists a structure to map the VM to physical memory. Nowadays that is a privileged structure.
/proc/[pid]/pagemap (since Linux 2.6.25)
This file shows the mapping of each of the process's virtual pages into physical page frames or swap area. It contains one 64-bit value for each virtual page, with the bits set as follows:
- 63 If set, the page is present in RAM.
- 62 If set, the page is in swap space
- 61 (since Linux 3.5) The page is a file-mapped page or a shared anonymous page.
- 60–57 (since Linux 3.11) Zero
- 56 (since Linux 4.2) The page is exclusively mapped.
- 55 (since Linux 3.11) PTE is soft-dirty (see the kernel source file Documentation/adminguide/mm/soft-dirty.rst).
- 54–0 If the page is present in RAM (bit 63), then these bits provide the page frame number, which can be used to index /proc/kpageflags and /proc/kpagecount. If the page is present in the swap (bit 62), then bits 4–0 give the swap type, and bits 54–5 encode the swap offset.
Further information (https://www.kernel.org/doc/Documentation/vm/pagemap.txt)
Summary
As a summary, we can understand the Virtual Memory is an architectural component for hardware systems and operating systems. The concepts behind are resource optimization, security, and intercommunication. Without VM architecture the modern systems could not be cost-efficient.
References
Kernel.org - (pagemap)
Linux man - proc(5), malloc(3), ipcs(1)
Wikipedia (Virtual Memory)