These problems should be done on your own. They are not to be turned in. Getting help from AI (besides being ruled out by the course policies) will actually hurt you, since the point of these questions isn’t so you can deliver an answer somewhere and check a box; it’s for you to gain the practice and experience of working through the problems. You will need that general skill (of absorbing something by practicing it) in life, and in this semester you will need the specific problem-solving skills that are emphasized in these homeworks.
TLB, page faults
Assume that the assembly code below is executed after a context switch. Make the following additional assumptions:
- The TLB is flushed (emptied) after context switch.
- Suppose all data pages (in the example below: 0x200000, 0x300000) are stored on disk when instruction 0x500 is executing.
- There is no prefetching.
[context switch]
0x500 movq 0x200000, %rax # move data in address 0x200000 to register %rax
0x504 incq %rax, 1 # add one to %rax
0x508 movq %rax, 0x300000 # move register %rax to memory at address 0x300000
Answer the following questions:
- How many TLB misses will happen, and for which pages?
- How many page faults will happen, and for which pages?
TLBs
Consider a TLB which can store 4 mappings (the TLB is fully associative, meaning that any entry can store any mapping; if this parenthetical confuses you, you can ignore it). Below you will write C code to compute the sum of all integers in an array a
, which is 6 pages in length; you will do this in a way that maximizes the number of TLB misses (equivalently, minimizes the number of TLB hits).
A few things to note:
- The array is allocated to be page aligned, meaning that the first element in the array is at the beginning of a page.
- Your program can assume that the constant
PAGE_SIZE
is the size of a page in bytes and thatsizeof(int)
is the size of an integer. - You can ignore the effect on the TLB from fetching code; in other words, you can assume that the only memory references that affect the TLB are loads from array
a
. (In real systems, there are separate TLBs for instructions and data; this question is focusing on the data TLB.) - You can further assume that the processor does nothing else while your code is running; that is, you don’t need to worry about TLB flushes from context switches.
uint64_t tlb_unfriendly() {
int *a = page_alloc(6 * PAGE_SIZE);
populate_array(a); // sets the integers in the array
uint64_t sum = 0;
/* YOUR CODE HERE: compute sum in the most TLB-unfriendly way possible */
return sum;
}
Uses of page faults
In this problem, you will describe how the implementation of malloc() can exploit paging so that the system (as a whole) can detect certain kinds of out of bound accesses; an out of bound access is when a process references memory that is outside an allocated range. In this problem we focus on overruns. Consider this code:
int *a = malloc(sizeof(int) * 100); /* allocates space for 100 ints */
a[0] = 5; /* This is a legal memory reference */
a[99] = 5; /* This is also a legal memory reference */
a[100] = 6; /* This is an overrun, and is an illegal memory reference. */
When the above executes, the process would ideally page fault as a result of an illegal memory reference, at which point the kernel would end the process.
Assume that malloc()
is a system call, so its implementation is inside the operating system, and thus can manipulate the virtual address space of the process.
Describe how the implementation of malloc()
can arrange for page faults when there are overruns like the one above.
mmap()
Consider the following code excerpt that uses mmap()
:
int main(int argc, char* argv[]) {
// ...
// readme.txt is a file that is 5KB in length.
int fd = open("readme.txt", O_RDWR, 0);
char *map1 = (char*)mmap(NULL, 5120, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
char *map2 = (char*)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// assume that neither mmap call fails
char x = 0;
char y = 0;
// Line 1
for (int i = 0; i < 5120; i++) {
x = map1[i];
y = map2[i % 4096];
}
// Line 2
// ...
}
// The signature of mmap is:
//
// void* mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
//
// If addr is NULL, the kernel chooses the start virtual address.
//
// If not already in memory, disk blocks are fetched into physical pages on access.
//
// Each separate call to mmap() with the same fd maps the file in a separate
// location and does not undo prior mappings. This means that in the example
// above, the file can be read and written from multiple virtual addresses
// within the same address space.
Assume the operating system minimizes the number of virtual and physical pages required in order to implement mmap()
.
How many last-level (level-4) page table entries are created or modified as a result of the two
mmap()
calls?At line 2, how many physical pages are mapped into the process as a result of the
mmap()
calls?