Show all solutions
Exam scheduling
You must take this test in person at Winokur Lecture Hall, SEC 1.321, Allston.
Exam scheduling
You must take this test in person at William James Hall B1.
Exam scheduling
You must take this test in person at Maxwell Dworkin G115.
Exam scheduling
You are scheduled to take the test at home. After your exam period is over, you will record a short video explaining one of your answers, make a shareable URL for that video, and update the exam to include that URL.
Exam scheduling
You are scheduled to take the test at the DAO Alternative Testing Center. Press Start to begin the exam when you are in the testing center and ready.
Rules
The exam is open book, open note, limited open computer. You may access the textbook and your own notes. You may also use computers or electronic devices to access your own class materials and public class materials. Specifically:
- You may access your own notes and problem set code electronically.
- You may access Internet sites on which your own notes and problem set code are stored.
- You may access a browser and a PDF reader.
- You may access the course site.
- You may access pages directly linked from the course site, including our lecture notes, exercises, and section notes, and reference material, such as cppreference.com.
- You may access our lecture, problem set, and section code.
- You may run a C++ compiler, including an assembler and linker.
- You may access manual pages and common utilities, including calculators and a Python interpreter.
However:
- You may not access class discussion forums or other question forums such as Stack Overflow.
- You may not access an on-line disassembler or compiler explorer.
- You may not access solutions from any previous exam, by paper or computer, except for those on the course site.
- You may not broadly search the Internet for answers to questions or access other courses’ materials. Stick to our course site.
- You may not interact with AI models (e.g., ask ChatGPT a question).
- You absolutely may not contact other humans with questions about the exam—whether in person, via IM, or by posting questions to forums—with the exception of course staff.
Any violations of this policy are breaches of academic honesty and will be treated accordingly. Please appreciate our flexibility and behave honestly and honorably.
Additionally, students are taking the exam at different times. Do not post publicly about the exam until given permission to do so by course staff. This includes general comments about exam difficulty.
Questions
If you have questions during the exam, write us email at
cs61-staff@g.harvard.edu
. Don’t use the Edboard. We may not notice
Edboard posts, and you aren’t supposed to access the Edboard anyway.
If unsure, briefly explain your thinking for potential partial credit.
Completing your exam
You have 90 minutes to complete the exam starting from the time you press Start. Different students may have different exams. Enter your answers directly in this form. A green checkmark appears when an answer is saved.
Students with extra time accommodations: If your extra time is not reflected next to the start button, contact course staff and wait for confirmation before pressing it.
You should not need to attach a drawing or other longer notes, but you may do
so by adding them to a test2
subdirectory in your cs61-f24-psets
repository. Make sure you push your changes to GitHub before time is up, and
explain in this form where we should look at your notes.
There is no need to explicitly “submit” the exam. Your changes are automatically saved with timestamps. We’ll grade the latest version saved within your exam window.
Notes
Assume a Linux operating system running on the x86-64 architecture unless otherwise stated. If you get stuck, move on to the next question.
Some figures and code samples can be pinned to your browser so you can keep them visible as you scroll through the test. Push the 📌 and then drag the pinned figure where you want it.
1. Our governing body (22 points)
The Harvard Corporation has attempted to complete pset 3. Their implementation has a lot of problems, but they refuse to admit they’ve done anything wrong. Help prove that they have problems by attacking their implementation.
QUESTION 1A. After step 3 (general process address spaces), the Harvard Corporation’s WeensyOS looks like this:
Note that addresses below 0x100000 appear in reverse video in the virtual memory displays.
This OS does not implement kernel isolation correctly. How can you tell? Explain briefly.
QUESTION 1B. The Corporation uses this code to set up a process’s initial
page table in process_setup
.
ptable[pid].pagetable = kalloc_pagetable();
for (uintptr_t addr = 0; addr < PROC_START_ADDR; addr += PAGESIZE) {
int perm = PTE_P | PTE_W | PTE_U;
if (addr == 0) {
perm = 0;
}
int r = vmiter(ptable[pid].pagetable, addr).try_map(addr, perm);
assert(r == 0);
}
Change this code to fix the problem in the previous question (without otherwise breaking the OS).
QUESTION 1C. After a bit more hacking, the Corporation’s kernel implementation of
syscall_page_alloc
looks like this.
int syscall_page_alloc(uintptr_t addr) {
if (addr % PAGESIZE != 0) {
return -1;
}
void* ptr = kalloc(PAGESIZE);
vmiter it(current, addr);
it.map(ptr, PTE_P | PTE_W | PTE_U);
assert(physpages[addr / PAGESIZE].refcount == 0);
++physpages[addr / PAGESIZE].refcount;
memset((void*) addr, 0, PAGESIZE);
return 0;
}
Describe at least three substantially different problems with this code that should be fixed by the time the Corporation completes the pset.
QUESTION 1D. The memory maps in the first question show WeensyOS running the four allocator processes after step 3. Each allocator process contains three segments—code, data, and stack—where the code segment occupies 3 pages and the data and stack segments occupy one page each. Each allocator process also has a four-level page table.
Here’s a duplicate of the initial portion of the physical memory map from the that diagram, in text format for screen readers. Bold shows characters with red backgrounds.
PHYSICAL MEMORY 0x000000 R111111111222222222333333333444444444K44342344444244424124344344 0x040000 KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK34443242333334434341344241K
Which physical pages are most likely to contain process 2’s page table pages? Either refer to features of the physical memory map or give specific addresses.
QUESTION 1E. Which physical pages are most likely to contain process 2’s code? Either refer to features of the physical memory map or give specific addresses.
2. Kernel multiple choice (16 points)
QUESTION 2A. %cr3
: List all that apply.
- The value in the
%cr3
register must be a multiple of 1000. - The value in the
%cr3
register must be a multiple of 0x1000. - The value in the
%cr3
register is interpreted as a virtual address. - The value in the
%cr3
register is interpreted as a physical address. - The value in the
%cr3
register is not interpreted as an address. - The value in the
%cr3
register is the same for every process. - None of the above.
QUESTION 2B. Which of the following statements are required for correct kernel isolation in WeensyOS? List all that apply.
- Processes cannot directly change the
%rax
register. - Processes cannot directly change the
%cr3
register. - Processes cannot modify physical memory containing kernel code.
- Processes cannot modify physical memory containing their own process code.
- Processes cannot transfer control to arbitrary locations in the kernel.
- Processes cannot transfer control to the kernel at all.
- None of the above.
QUESTION 2C. Page table entries: List all that apply.
- Page table entries are 8 bytes big.
- Page table entries are stored in arrays called page table pages.
- Page table entries are initially assigned by software, but interpreted most frequently by hardware.
- Page table entries are divided into components called the offset, the level-1 index, the level-2 index, the level-3 index, and the level-4 index.
- Page table entries are divided into flags and an optional physical address.
- Page table entries may be modified by the kernel in response to a system call.
- None of the above.
QUESTION 2D. WeensyOS stacks: List that all apply. (Assume a WeensyOS that has correctly completed all steps of Problem Set 3.)
- A WeensyOS running N processes uses N physical pages of memory for stacks.
- A WeensyOS running N processes uses N+1 physical pages of memory for stacks.
- All WeensyOS process stacks are located at the same virtual memory address.
- All WeensyOS process stacks are located at the same physical memory address.
- Each WeensyOS process page table has exactly one present mapping for a stack page.
- Each WeensyOS process page table has exactly one user-visible present mapping for a stack page.
- None of the above.
3. Blocks and pages (22 points)
For your reference: Table of powers of 2
QUESTION 3A. How big is an x86-64 page?
QUESTION 3B. How many page table entries fit in an x86-64 page?
QUESTION 3C. Intel has produced a new architecture, the 🤮96-64. This architecture’s virtual memory system resembles x86-64’s—it has 4-level page tables and 64-bit virtual addresses—but its page size is 8192 bytes.
What is the size in bits of the page offset part of a 🤮96-64 address?
QUESTION 3D. What is the size in bits of the level-1 index part of a 🤮96-64 address? (Level-2, 3, and 4 indexes will have the same size in bits.)
QUESTION 3E. In x86-64, which we studied in class, virtual addresses have only 48 meaningful bits, even though their size is 8 bytes (64 bits). This is a consequence of the x86-64 page table design: a 4-level page table with x86-64’s offset and index sizes can distinguish only 48 bits.
How many meaningful bits does a 🤮96-64 virtual address contain?
QUESTION 3F. A run of strace stdio-cat61
on a 🤮96-64 machine produces
results like this.
openat(AT_FDCWD, "/tmp/cs61-22f7/outputs/out.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4
fcntl(4, F_GETFL) = 0x8001 (flags O_WRONLY|O_LARGEFILE)
fstat(3, {st_mode=S_IFREG|0644, st_size=10485760, ...}) = 0
read(3, "ax's\nAkbar\nAkbar's\nAkhmatova\nAkh"..., 4096) = 4096
fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
read(3, "he's\nAndromeda\nAndromeda's\nAndro"..., 4096) = 4096
write(4, "ax's\nAkbar\nAkbar's\nAkhmatova\nAkh"..., 4096) = 4096
read(3, "sia's\nAsiatic\nAsiatic's\nAsiatics"..., 4096) = 4096
write(4, "he's\nAndromeda\nAndromeda's\nAndro"..., 4096) = 4096
read(3, "dos\nBarbados's\nBarbara\nBarbara's"..., 4096) = 4096
write(4, "sia's\nAsiatic\nAsiatic's\nAsiatics"..., 4096) = 4096
read(3, "\nBest\nBest's\nBetelgeuse\nBetelgeu"..., 4096) = 4096
write(4, "dos\nBarbados's\nBarbara\nBarbara's"..., 4096) = 4096
How big is the corresponding stdio cache in 🤮96-64 pages?
QUESTION 3G. The following four strace
fragments are from different
variants of cat61
, running on the same input file as the previous question.
Which of these variants corresponds to a correct stdio-like cache with
size of exactly one 🤮96-64 page? Explain briefly. Note that read
system
calls are not present in these strace fragments.
-
cat61a:
openat(AT_FDCWD, "/tmp/cs61-22f7/outputs/out.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4 fcntl(4, F_GETFL) = 0x8001 (flags O_WRONLY|O_LARGEFILE) fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 write(4, "ax's\nAkbar\nAkbar's\nAkhmatova\nAkh"..., 4096) = 4096 write(4, "he's\nAndromeda\nAndromeda's\nAndro"..., 4096) = 4096 write(4, "sia's\nAsiatic\nAsiatic's\nAsiatics"..., 4096) = 4096 write(4, "dos\nBarbados's\nBarbara\nBarbara's"..., 4096) = 4096
-
cat61b:
openat(AT_FDCWD, "/tmp/cs61-22f7/outputs/out.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4 fcntl(4, F_GETFL) = 0x8001 (flags O_WRONLY|O_LARGEFILE) fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 write(4, "ax's\nAkbar\nAkbar's\nAkhmatova\nAkh"..., 8192) = 8192 write(4, "he's\nAndromeda\nAndromeda's\nAndro"..., 8192) = 8192 write(4, "sia's\nAsiatic\nAsiatic's\nAsiatics"..., 8192) = 8192 write(4, "dos\nBarbados's\nBarbara\nBarbara's"..., 8192) = 8192
-
cat61c:
openat(AT_FDCWD, "/tmp/cs61-22f7/outputs/out.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4 fcntl(4, F_GETFL) = 0x8001 (flags O_WRONLY|O_LARGEFILE) fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 write(4, "ax's\nAkbar\nAkbar's\nAkhmatova\nAkh"..., 8192) = 8192 write(4, "sia's\nAsiatic\nAsiatic's\nAsiatics"..., 8192) = 8192 write(4, "\nBest\nBest's\nBetelgeuse\nBetelgeu"..., 8192) = 8192 write(4, "is\nCalais's\nCalcutta\nCalcutta's\n"..., 8192) = 8192
-
cat61d:
openat(AT_FDCWD, "/tmp/cs61-22f7/outputs/out.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4 fcntl(4, F_GETFL) = 0x8001 (flags O_WRONLY|O_LARGEFILE) fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 write(4, "ax's\nAkbar\nAkbar's\nAkhmatova\nAkh"..., 16384) = 16384 write(4, "\nBest\nBest's\nBetelgeuse\nBetelgeu"..., 4096) = 4096 write(4, "rant\nBrant's\nBraque\nBraque's\nBra"..., 12288) = 12288 write(4, "cero's\nCid\nCid's\nCimabue\nCimabue"..., 4096) = 4096
QUESTION 3H. Unlike the stdio cache, the buffer cache has no fixed size in pages. What entity manages the size of the buffer cache, and what limits the buffer cache’s size?
4. Bad I/O caches (15 points)
This question involves implementations of io61
calls like io61_readc
,
io61_read
, and io61_write
. You may want to refer to the problem set code and to the single-slot cache section material.
QUESTION 4A. In the handout code, io61_readc
calls read
, causing one
system call per byte. This student has tried to speed up io61_readc
by having it
call io61_read
. What’s wrong with their implementation? Explain briefly.
int io61_readc(io61_file* f) {
unsigned char ch;
ssize_t nr = io61_read(f, &ch, 1);
return nr == 1 ? ch : EOF;
}
ssize_t io61_read(io61_file* f, unsigned char* buf, size_t sz) {
size_t nr = 0;
while (nr != sz) {
int ch = io61_readc(f);
if (ch == EOF) {
break;
}
buf[nr] = ch;
++nr;
}
return nr;
}
QUESTION 4B. This version of io61_read
uses a single-slot cache based on
our section material. What’s wrong with this implementation? Explain briefly.
struct io61_file {
int fd, mode;
static constexpr size_t bufsize = 4096;
unsigned char cbuf[bufsize];
off_t tag, pos_tag, end_tag;
};
ssize_t io61_read(io61_file* f, unsigned char* buf, size_t sz) {
size_t nr = 0;
while (nr != sz && f->pos_tag != f->end_tag) {
buf[nr] = f->cbuf[f->pos_tag - f->tag];
++nr;
++f->pos_tag;
}
return nr;
}
QUESTION 4C. This version of io61_flush
attempts to flush the cbuf
cache
for writing. What’s wrong with it? Explain briefly.
int io61_flush(io61_file* f) {
while (f->mode == O_WRONLY && f->end_tag > f->tag) {
ssize_t nw = write(f->fd, f->cbuf, f->bufsize);
f->tag = f->pos_tag = f->tag + nw;
}
return 0;
}
5. Reference strings (20 points)
For these questions, write reference strings as sequences of numbers, such as “1 2 3 4 1 2 5”.
QUESTION 5A. What is the maximum hit rate possible for the following reference string, over all possible cache slot counts and eviction algorithms? Assume the cache is initially empty, and assume no prefetching.
1 2 3 1 4 1 2 3 4 5
QUESTION 5B. Calculate the hit rate for the following reference string, a 3-slot fully-associative cache, and the Least Frequently Used eviction policy. (This policy evicts the slot whose contents were used least often. In case of a tie, it evicts the slot whose contents were used least recently.)
1 2 3 1 4 1 2 3 4 5
QUESTION 5C. Write a reference string for which the Least Frequently Used eviction policy has a higher hit rate than the First-In, First-Out eviction policy. Assume the cache is fully associative; you may choose the number of slots (we’ll assume 3 slots unless you state otherwise).
QUESTION 5D. Write a reference string for which the Least Frequently Used eviction policy has a higher hit rate than the Least Recently Used eviction policy. Assume the cache is fully associative; you may choose the number of slots (we’ll assume 3 slots unless you state otherwise).
QUESTION 5E. In reference strings corresponding to sequential access, such as “1 2 3 4 5 6 7 8 9 …”, every address is unique. One might assume that reference strings with no repeats are uncacheable (have max hit rate 0%), but we know from lecture and pset 4 that caches enormously improve the performance of sequential access. Which cache optimizations are involved in caching sequential access, especially for reads? Explain briefly.
6. The end (5 points)
QUESTION 6A. What topics are you interested in spending more time on? Any answer but no answer will receive full credit.
Video
QUESTION 6B. This button will activate 80 minutes after you start the test. Once you click it, it will reveal the problem number for which you should submit a three-minute-or-less explanation video. Video instructions are here.
QUESTION 6C. Prepare your video on Part 1, Our governing body. Your video should describe your general approach, then give more depth into a Part 1 question of your choice.
QUESTION 6D. Prepare your video on Part 2, Kernel multiple choice. Your video should describe your general approach, then give more depth into a Part 2 question of your choice.
QUESTION 6E. Prepare your video on Part 3, Blocks and pages. Your video should describe your general approach, then give more depth into a Part 3 question of your choice.
QUESTION 6F. Prepare your video on Part 4, Bad I/O caches. Your video should describe your general approach, then give more depth into a Part 4 question of your choice.
QUESTION 6G. Prepare your video on Part 5, Reference strings. Your video should describe your general approach, then give more depth into a Part 5 question of your choice.
QUESTION 6H. Once you have recorded your 3-minute video, copy the unlisted link to that video here. You can obtain a link by, for example, uploading the video to YouTube as “Unlisted,” so that only those with the link can view it; or uploading it to Google Drive, with permissions set so that only those with the link can view it.