 Another key resource which the operating system needs to manage is memory. And so the mystery is here, how does the operating system make sure that a process can only access its own memory? When a process is running, it should only be able to read and write the addresses which are allotted to it by the operating system. When the operating system code is running, however, everything should be fair game because, well, presumably the operating system knows what it's doing. So how does the operating system keep a process inside its box? Well, again, the operating system here needs cooperation from the hardware. Modern CPUs can be set into a mode in which, when an instruction specifies a particular address, the hardware, the CPU, translates it into some other address. So the addresses specified in machine code actually become virtual addresses. They are not the actual addresses, they are not the actual physical addresses that correspond to memory addresses. Now, when the operating system code is running, the CPU is usually in just the normal mode where an address specified in an instruction corresponds to an actual physical address. But when the operating system is about to hand execution off to a process, it first configures the CPU into this special mode such that, when that process executes, addresses only correspond to bytes in memory which the operating system wants that process to be able to use. This whole scheme is called virtual memory. Virtual memory also happens to solve another sticky problem with memory addresses, and that is this. When I write a program to be executed by an operating system, I have to expect that there are going to be other programs in memory at the same time as well as the operating system code itself. So the question is then, which addresses should I use? Because if the addresses in my machine code correspond to actual physical addresses in the system RAM, then somehow my program needs to make sure it only uses addresses which no one else is going to use. Trying to do this though would just be totally impractical. Inevitably you'd end up with many programs trying to use the same addresses and would mean that you couldn't run those two programs at the same time. The virtual memory system neatly solves this problem because what it means is that in effect each program, each process, has its own address space. Because the addresses specified in code get translated into other actual addresses, when two different programs use the same address in their code, that address doesn't have to correspond to the same byte of memory. Say we have some process and let's say that on the system virtual addresses are 32 bits in size. So the lowest address this process might use is of course 000000, but the biggest address is ffffff. As we discussed earlier, a process has three basic kinds of things to put in memory. It has first the code section, a stack, and then it has some amount of heap. The code section is almost always contiguous and quite often it's simply put at the bottom of the address space starting at address 0 that is. And the stack space is actually placed at the very top. When we put the stack at the top like this, we actually have the stack grow downwards so new frames get placed below the previous frame instead of above. It otherwise works just the same. It's just turned upside down. The heap section, as you'll notice, often ends up broken up. It's not necessarily contiguous. In any case, when the process runs and it uses addresses, what actually happens is that before the operating system jumped execution to this process, we configured the CPU and set up what are called its memory tables such that these chunks of virtual addresses actually correspond to various sections of actual physical RAM, the physical addresses of RAM. So for instance, when the CPU executes machine instruction in the process, which reads a byte of memory in the stack, the CPU actually first translates that virtual address into an actual address, a physical address, using the memory tables. And note here that when the operating system maps sections of a processes address space, its virtual addresses, when it maps them into physical addresses, it doesn't have to put everything in the same order or contiguous, it can really just choose any chunk of memory that it wants. Now, this whole arrangement wouldn't really be practical if every single address in every single process was uniquely mapped to a unique byte of RAM, a physical memory. If the scheme worked that way, then it would mean that the memory table, which the operating system keeps around for every single process, each table would have to store an address for every single byte used in the process, and that would be insane. On a system with 32-bit addresses, that means each address is 4 bytes, and so you'd be storing 4 bytes for every single byte that you want to actually use in a process. So that's not how virtual memory works. Instead, virtual addresses and physical addresses are treated in chunks called pages. Each one of these pages is something significantly larger than just a single byte. On the Intel processors, for instance, each page is 4 kibby bytes in size. And so, in the memory tables, we don't map individual virtual addresses to individual physical addresses. Instead, individual pages of virtual addresses get mapped to pages of physical addresses. Virtual memory systems have another neat bonus, and that is that it allows the system, the operating system and the hardware working together, to take the memory used by a process, either whole or in part, and swap it out to disk. That is, to take the process in whole or in part, out of physical memory, out of system RAM, and instead store that stuff on some storage device like a hard disk. To do this, the operating system simply copies pages from RAM into some place on the hard drive, and in that processes memory table, it marks those pages as swapped. The next time, then, when the process is running, and it attempts to use one of those addresses that has been swapped out, well, that triggers a hardware exception, which then runs code in the operating system that loads the relevant page back into actual RAM. It then marks that page as no longer swapped, and then the process can use that page as normal. So, the gain here is that when processes can be swapped out onto hard disk or some other storage medium, the processes running on our system can either individually or in total consume more memory than our system actually has RAM for. Of course, on the downside, the hard drive still is orders of magnitude slower than system memory, so programs usually become painfully slow if they constantly have to use pages which have been swapped out. There are many examples, though, of some interactive programs which may consume a lot of memory, but generally don't need to do anything when the user is not directly interacting with them. Like, say, when you have many tabs open in a web browser, well, a lot of those tabs might sit unused for minutes if not hours and days, so if the data for those tabs get swapped out, that's perfectly fine. All it means is that the next time we click on that tab, there may be a slight delay as that data has to be swapped back into memory. The ability to swap to disk also tends to make programs more reliable, because, say, if our system has one gigabyte of physical RAM, well, load up a few applications like, say, Photoshop in a web browser and open a lot of tabs and files, and pretty soon you've run out of RAM. When most programs try to acquire more memory, but there's no more memory to be had, the program generally has no choice but to just abnormally abort. When the system has a generous amount of swap space, this gives the programs a lot more breathing room. Instead of programs outright failing when they're using too much memory, they just might start running slowly because they have to keep using hard drive space instead of actual physical RAM.