Autumn 2021
Jerry Cain
PDF
The page table stored on behalf of a single process might look like this:
7FFFFFFF80234 maps to 835432
0000DDEE65111 maps to 45D834
many additional entries
0000A9CF9AAAF maps to 12387B
0x7FFFFFFF80234230 & 0xFFF = 0x230
0x230 | (0x835432 << 12) = 0x835432230
Many, including the authors of your primary textbook, view the hard drive as the physical memory and main memory as a cache storing those pages from disk currently being accessed. This viewpoint has many advantages!
Executables (e.g. ls, pipeline-test, etc) are stored on disk and loaded into main memory when a process is created to run that executable.
As a general rule, every single virtual page of memory uniquely maps to a physical page of memory. However, Linux often ignores that rule for code and rodata segments, since code and global constants are read only.
In particular, multiple processes running emacs—there were 17 such processes running on myth63 at the time I constructed this slide—all map their code segment to the same pages in physical memory.
As main memory becomes saturated, the OS will evict pages that have become inactive and flush them to the hard drive, often in a special file called a swap file.
For instance, at the time of I created this slide, myth53 had one process running vi that had been idle for 5 days.
The VM page mappings of that vi process have almost certainly been discarded and will only be remapped when the user interacts with that vi session again (or when someone else launches vi). The state of memory needed to eventually continue would have, at some point, been written to disk.
static void recharge() {
cout << oslock << "I recharge by spending time alone." << endl << osunlock;
}
static const size_t kNumIntroverts = 6;
int main(int argc, char *argv[]) {
cout << "Let's hear from " << kNumIntroverts << " introverts." << endl
thread introverts[kNumIntroverts]; // declare array of empty thread handles
for (thread& introvert: introverts)
introvert = thread(recharge); // move anonymous threads into empty handles
for (thread& introvert: introverts)
introvert.join();
cout << "Everyone's recharged!" << endl;
return 0;
}
static void greet(size_t id) {
for (size_t i = 0; i < id; i++) {
cout << oslock << "Greeter #" << id << " says 'Hello!'" << endl << osunlock;
struct timespec ts = {
0, random() % 1000000000
};
nanosleep(&ts, NULL);
}
cout << oslock << "Greeter #" << id << " has issued all of his hellos, "
<< "so he goes home!" << endl << osunlock;
}
static const size_t kNumGreeters = 6;
int main(int argc, char *argv[]) {
cout << "Welcome to Greetland!" << endl;
thread greeters[kNumGreeters];
for (size_t i = 0; i < kNumGreeters; i++) greeters[i] = thread(greet, i + 1);
for (thread& greeter: greeters) greeter.join();
cout << "Everyone's all greeted out!" << endl;
return 0;
}