logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

How-does-virtual-memory-work.md (4758B)


  1. ---
  2. date: 2018-10-29
  3. layout: page
  4. title: How does virtual memory work?
  5. tags: ["instructional"]
  6. ---
  7. Virtual memory is an essential part of your computer, and has been for several
  8. decades. In my [earlier article on pointers][pointers], I compared memory to a
  9. giant array of octets (bytes), and explained some of the abstractions we make
  10. on top of that. In actual fact, memory is more complicated than a flat array of
  11. bytes, and in this article I'll explain how.
  12. [pointers]: /2016/05/28/Understanding-pointers.html
  13. An astute reader of my earlier article may have considered that pointers on,
  14. say, an x86_64 system, are 64 bits long[^1]. With this, we can address up to
  15. 18,446,744,073,709,551,616 bytes (16 exbibytes[^2]) of memory. I only have 16
  16. GiB of RAM on this computer, so what gives? What's the rest of the address space
  17. for? The answer: all kinds of things! Only a small subset of your address space
  18. is mapped to physical RAM. A system on your computer called the MMU, or Memory
  19. Management Unit, is responsible for managing the abstraction that enables this
  20. and other uses of your address space. This abstraction is called virtual memory.
  21. [^1]: Fun fact: most x86_64 implementations actually use 48 bit addresses internally, for a maximum theoretical limit of 256 TiB of RAM.
  22. [^2]: I had to look that SI prefix up. This number is 2<sup>64</sup>, by the way.
  23. The kernel interacts directly with the MMU, and provides syscalls like
  24. [mmap(2)][mmap] for userspace programs to do the same. Virtual memory is
  25. typically allocated a page at a time, and given a purpose on allocation, along
  26. with various flags (documented on the mmap page). When you call `malloc`, libc
  27. uses the mmap syscall to allocate pages of heap, then assigns a subset of that
  28. to the memory you asked for. However, since many programs can run concurrently
  29. on your system and may request pages of RAM at any time, your physical RAM can
  30. get fragmented. Each time the kernel hits a context switch[^3], it swaps out
  31. the page table for the next process.
  32. [^3]: This means to switch between which process/thread is currently running on a single CPU. I'll write an article about this sometime.
  33. [mmap]: http://man7.org/linux/man-pages/man2/mmap.2.html
  34. This is used in this way to give each process its own clean address space and to
  35. provide memory isolation between processes, preventing them from accessing each
  36. other's memory. Sometimes, however, in the case of shared memory, the same
  37. physical memory is deliberately shared with multiple processes. Many pages can
  38. also be any combination readable, writable, or executable - the latter meaning
  39. that you could jump to it and execute it as native code. Your compiled program
  40. is a file, after all - mmap some executable pages, load it into memory, jump to
  41. it, and huzzah: you're running your program[^4]. This is how JITs, dynamic
  42. recompiling emulators, etc, do their job. A common way to reduce risk here,
  43. popular on *BSD, is enforcing W^X (writable XOR executable), so that a page can
  44. be either writable or executable, but never both.
  45. [^4]: There are actually at least a dozen other steps involved in this process. I'll write an article about loaders at some point, too.
  46. Sometimes all of the memory you think you have isn't actually there, too. If you
  47. blow your RAM budget across your whole system, swap gets involved. This is when
  48. pages of RAM are "swapped" to disk - as soon as your program tries to access it
  49. again, a page fault occurs, transferring control to the kernel. The kernel
  50. restores from swap, damning some other poor process to the fate, and returns
  51. control to your program.
  52. Another very common use for virtual memory is for memory mapped I/O. This can
  53. be, for example, mapping a file to memory so you can efficiently read and write
  54. to disk. You can map other sorts of hardware, too, such as video memory. On 8086
  55. (which is what your computer probably pretends to be when it initially
  56. boots[^5]), a simple 96x64 cell text buffer is available at address `0xB8000`.
  57. On my TI-Nspire CX calculator, I can read the current time from the real-time
  58. clock at `0x90090000`.
  59. [^5]: You can make it stop pretending to do this with [an annoying complicated sequence of esoteric machine code instructions](https://wiki.osdev.org/Protected_Mode). An even more annoying sequence is required to [enter 64-bit mode](https://wiki.osdev.org/Setting_Up_Long_Mode). It gets even better if you want to set up [multiple CPU cores](https://wiki.osdev.org/Symmetric_Multiprocessing)!
  60. In summary, MMUs arrived almost immediately on the computing scene, and have
  61. become increasingly sophisticated ever since. Virtual memory is a powerful tool
  62. which grants userspace programmers elegant, convenient, and efficient access to
  63. the underlying hardware.