All quizzes

All quizzes

Quiz 01

Question 1: In xv6, which of the following information is part of the process control block, represented in xv6 as struct proc?
Select all that apply

  1. the value of the user registers, if the program is currently running in user mode on a processor

  2. the value of the user registers, if the program is currently not running in user mode on a processor

  3. the contents of the user stack

  4. the list of files the process has open

Question 2: On a typical implementation of POSIX, using read() to read one byte from a file descriptor with a call like:

    read(fd, buffer, 1);

where the file descriptor is opened to a file on a hard disk will:

  1. cause the OS to request exactly one byte from the disk, then store that byte in the application's buffer

  2. cause read to return an error because the hard disk can only read data in larger blocks

  3. cause the OS to request a block of data (larger than one byte) from disk and store it in the OS's memory, then copy one byte of that into the application's buffer

  4. cause the OS to request a block of data (larger than one byte) from disk, then copy the entire block into the application's buffer

Question 3: Consider the following POSIX C program:

#include <unistd.h>
int main(void) {
    int i = 0;
    for (i = 0; i < 2; ++i) {
        pid_t p = fork();
        printf("%d", i);
    }
    return 0; 
}

Which of the following is a possible output of this program?
Select all that apply

  1. 001111

  2. 011101

  3. 011011

  4. 010111

Question 4: After a successful call to execv
Select all that apply

  1. execv returns the new process ID

  2. file descriptors other than 0, 1, 2 (stdin, stdout, and stderr) are closed

  3. instructions are executed at the beginning of main()

  4. an executable specified as an argument to execv is loaded

Quiz 03

Question 1: Linux's Completely Fair Scheduler adjusts timeslices based on the number of processes that are running. When there are a large number of processes running, this adjustment is likely to improve
Select all that apply

  1. the mean wait time experienced by processes

  2. the compute throughput of the system

  3. the fairness of the amounts of processor time given to each process over a long period of time

  4. the amount of stack space required in the kernel

Question 2: On a system which implements threads in the kernel, creating a thread requires allocating
Select all that apply

  1. space for an additional user stack

  2. space for an additional heap

  3. more space to store user register values

  4. space for an additional array of open files

Question 3: Consider the following function:

node *head = NULL;
void prepend(int new_value) {
    node *new_head = new node;
    new_head->value = new_value;
    new_head->next = head;
    head = new_head;
}

When running prepend simulatenously from multiple threads we find that one of the prepended values is not on the linked list head. What most likely happened?

  1. each thread had its own version of the global variable head and so each created its own list

  2. two threads running prepend read the same value of head

  3. one thread running prepend overwrote the other thread's new_head->next value as it copied new_head over head

  4. one thread's call to prepend overwrote the other thread's copy of the variable new_value

Question 4: Consider the following pthreads code:

int global = 100;
void *print_id(void *argument) {
    int value = (int*) argument;
    printf("value=%d; global=%d\n", value, global);
    return NULL;
}

int main() {
    pthread_t threads[2];
    ++global;
    for (int i = 0; i < 2; ++i) {
        pthread_create(&threads[i], NULL, print_id, (void *) i);
    }
    for (int i = 0; i < 2; ++i) {
        pthread_join(threads[i], NULL);
    }
    return 0;
}

Assume that reading from and writing to an int or void * is atomic and that within each thread, operations (such as loads and stores) are executed in the order they are written in the C code (that is, not reordered by the processor or compiler). Which of the following would be possible outputs of this program?
Select all that apply

  1. value=0; global=101
    value=1; global=101
    
  2. value=1; global=101
    value=0; global=101
    
  3. value=0; global=100
    value=1; global=100
    
  4. value=2; global=101
    value=2; global=101
    

Quiz 04

Question 1: In lecture we discussed a cache coherency mechanism where

  • each processor has its own cache
  • these caches and memory are all connected via a shared bus
  • each block in each cache is in either a Modified, Shared, or Invalid state.

Which of the following is true about such a system?
Select all that apply

  1. two processors can modify two memory locations that map to different parts of the same cache block at the same time (without waiting for each other)

  2. two processors can both hold the value of a single memory location in their caches at the same time

  3. after one processor finishes updating a value, another processor could still have an old version of the value cached

  4. when a value is written to the processors' caches, they must always write that value to memory immediately (by sending a message over the shared bus)

Question 2: We discussed a strategy to implement a "mutex" lock that puts waiting threads to sleep, allowing other threads to use the CPU, that internally used a spinlock (a lock that "spins" in an infinite loop checking the status of the lock). Which of the following was true about this spinlock?

  1. the spinlock is not locked the entire time the mutex lock is locked

  2. threads only acquire the spinlock after checking that acquiring the lock was likely to succeed without waiting

  3. the spinlock is released earlier than usual when another thread started waiting to lock the mutex lock

  4. this spinlock would be unlocked whenever the processor entered kernel mode

Suppose we are writing a multithreaded program which sometimes needs to retrieve data over the network from a single remote machine. To avoid overloading that machine, we want to make sure at most ten threads are retrieving data from the machine at a time.

Question 3: (see above) (see scenario above) To do this we can use a counting (that is, non-binary) semaphore, where we initialize the semaphore to _________, use the ____ operation before each thread starts retrieving data, and use the _____ operation after each thread finishes retrieving data.

  1. 10 / down / down

  2. 9 / down / down

  3. -10 / down / up

  4. 10 / down / up

  5. 9 / down / up

  6. 0 / down / up

  7. -10 / up / down

  8. 10 / up / down

  9. 9 / up / down

  10. 0 / up / down

  11. 0 / up / up

  12. -10 / up / up

  13. none of the above

Question 4: (see above) (see scenario above) To do this we can use a mutex lock, condition variable, and a counter variable tracking the number of threads currently retreving data that is only accessed while holding the mutex. In this case, threads would call Wait on the condition variable ______.

  1. before retrieving data, as long as no other threads are retrieving data

  2. before retrieving data, as long as exactly 9 other threads are retrieving data

  3. before retrieving data, as long as exactly 10 other threads are retrieving data

  4. after retrieving data, as long as exactly 9 other threads are retrieving data

  5. after retrieving data, as long as no other threads are retrieving data

Quiz 05

Question 1: Suppose we have a system where one thread runs a function HandleChange(old_value, new_value) every time other threads use the SetValue function to change a shared value from old_value to new_value with the following code using a mutex and condition variable:

int value;
bool value_changed;
pthread_mutex_t lock;
pthread_cond_t change_happened;
void WaitForAndHandleChanges() {
    pthread_mutex_lock(&lock);
    while (true) {
        int old_value = value;
        while (!value_changed) {
            pthread_cond_wait(&change_happened, &lock);
        }
        HandleChange(old_value, value);
        value_changed = false;
    }
}
void SetValue(int new_value) {
    pthread_mutex_lock(&lock);
    value = new_value;
    value_changed = true;
    pthread_cond_signal(&change_happened);
    pthread_mutex_lock(&unlock);
}

After using this code, we find that sometimes HandleChange() is run too few times when SetValue() is called twice in a row from different threads.

How can we modify the above code to make sure HandleChange() is run once each time SetValue() is called?

  1. change SetValue to use pthread_cond_broadcast instead of pthread_cond_signal

  2. add an additional condition variable that SetValue waits for as long as value_changed is true and WaitForAndHandleChanges signals after setting value_changed to false

  3. change the while(!value_changed) loop in WaitForAndHandleChanges to checkvalue == old_valueinstead of!value_changed`

  4. use different mutexes for value_changed and for value

  5. have WaitForAndHandleChanges lock and unlock the mutex in the while(true) loop instead of locking it before the loop and never calling pthread_mutex_unlock

Question 2: In lecture, we discussed a version of reader/writer locks that favored readers. This had the disadvantage of starving writers. Suppose we wanted to implement a policy that favored readers until the waiting writers have been waiting for too much time. To implement this policy, what strategy could we use?

  1. track the amount of time writers have been waiting in shared variables, and use this information to decide whether to wait in the lock functions and to decide whether to signal/broadcast to readers to writers in the unlock functions.

  2. track the amount of time writers have been waiting in shared variables, and use this information to decide whether to signal/broadcast to readers to writers in the unlock functions

  3. track the amount of time writers have been waiting in shared variables, and use this information to decide whether to wait in the lock functions

  4. to replace the "write lock" cond_wait with a version of cond_wait that automatically wakes up after a certain amount of time (like POSIX's pthread_cond_timedwait)

  5. have each writer track the amount of time it's been waiting in a local variable in the "write lock" function, and if this time exceeds the maximum amount of time, to break out of the while loop that calls cond_wait

Suppose we have a multithreaded chat server which has a data structure to represent each user, which contains the list of messages they recently sent and the list of messages they recently received. Each user's data structure has its own lock. A naive attempt to implementing sending a message from one user to another first locks the sending user, then the receiving user, then updates the two message lists, then unlocks each of the locks. This naive strategy can cause deadlocks.

Question 3: (see above) When might a deadlock occur because of the locks described above?
Select all that apply

  1. when two threads at almost the same time try to send messages from a user A to a user B

  2. when one thread tries to send a message from user A to user B while another tries to send a message from user B to user A

  3. when one thread tries to send a message from user A to user B while another tries to send a message from user A to user C

  4. when too many threads all try to send messages to user A at the same time

Question 4: (see above) What alternatives would avoid these deadlocks?
Select all that apply

  1. add locks for the list of sent messages and the list of received messages in addition to the lock for each user. The send operation acquires the lock for the sending user, then for the list of sent messages, then for the receiving user, then the list of received messages, then adds the messages, then releases the locks

  2. replace the locks for the entire user data structure with seperate locks for the list of sent messages and the list of received messages. The send operation acquires the lock for the list of sent messages of the sending user and the list of received messages of the receiving user, then adds the messages, then releases the locks

  3. instead of acquiring the lock for the sending user first, acquire the lock for whichever of the sending and receiving users has the lowest address first

  4. instead of acquire the lock for the sending user, then the receiving user, acquire the locks in the other order: first acquire the lock for the receiving user, then the sending user.

Quiz 06

Question 1: Before it starts any programs, xv6 sets up its page table so that 224MB starting at virtual address 0x80000000 corresponds to physical byte addresses starting with 0. (MB = 1024 * 1024 bytes.) It does this using 4K pages. How much space is requires for page tables where only these pages are valid? (Assume it uses 4K pages and the page table format used by xv6.)

  1. 28 pages (112KB)

  2. 29 pages (116KB)

  3. 32 pages (128KB)

  4. 56 pages (224KB)

  5. 57 pages (228KB)

  6. 224 pages (896KB)

  7. 225 pages (896KB)

  8. 256 pages (1024KB)

Question 2: The sbrk() system call has commonly been used to implement malloc(). On an OS like xv6 where this is true, for each program that is using malloc() the kernel must keep track of which of the following?
Select all that apply

  1. the address of the end of the heap

  2. the amount of space in the heap that is currently used

  3. the physical addresses that are assigned to each virtual page in the heap

  4. a linked list of free regions of the heap

Question 3: A page fault will occur when a process tries to access a virtual address whose page table entry is invalid (on x86, has the "present" flag set to 0). If the operating system determines that the memory access should be allowed to despite it not having a valid page table entry, it will most likely:

  1. perform a memory access to the same virtual address, but in kernel mode, then return from the page fault handler

  2. lookup the virtual address in the process's page table, convert it to a physical address, then use that physical addres to access the memory,t hen return from the page fault handler

  3. adjust the page table for the process so the memory access will work if attempted again, then return from the page fault handler

  4. reload the process from disk and restart it from the beginning of main()

Question 4: Suppose a Unix-like operating system uses 4K pages and the 32-bit x86 page table scheme we discussed in class. A process is running on this OS where in its page table: * virtual addresses 0x1000 through 0x9FFF (9 pages) are valid and writable and used to hold code and global variables * virtual addresses 0x10000 through 0x10FFF (1 page) are valid and writable and used for the heap, and * virtual addresses 0x7FFF0000 through 0x7FFFFFFF (16 pages) are valid and writable and used for the main thread's stack, and * every other virtual address is invalid (To simplify these questions, we will ignore any kernel-only page table entries.)

This process invokes the fork() system call, then the child process modifies addresses 0x7FFFEFF8 through 0x7FFFF016 on its stack.

If the system uses copy-on-write to implement fork(), excluding pages that hold page tables and kernel data structures, how many new physical pages will be allocated as a result of the call to fork() and subsequent modification of the child process's stack?

  1. 0 pages

  2. 1 page

  3. 2 pages

  4. 3 pages

  5. 16 pages

  6. 17 pages

  7. 26 pages

  8. unknown; it depends on what instructions the child uses to modify the stack

  9. none of the above; the child process would crash (e.g. segfault)

Quiz 07

Question 1: Linux, and most operating systems which use memory to cache data on disk, maintains a mapping from physical pages to page table entries that reference this page. What is true about this mapping?
Select all that apply

  1. it is read by the processor when the program accesses a virtual address that corresponds to a valid page

  2. it is read by the operating system when it changes what file's data will be stored in that physical page

  3. each physical page has at most one corresponding page table entry, so the mapping is one to zero or one

Question 2: Suppose a process runs the following code on a Unix-like operating system:

    int fd = open("/tmp/file.dat", O_RDWR);
    char *data = mmap(NULL, 8 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

The calls to open and mmap succeed and mmap returns 0x999000, which is the address of the first byte of virtual page 0x999. Pages on this system are 4096 bytes.

Which of the following is definitely true?
Select all that apply

  1. immediately after the mmap call, the page table entry for virtual page 0x999 is valid

  2. immediately after accessing address 0x99A000 after this mmap call, the page table entry for virtual page 0x999 would be valid

  3. after the mmap call, attempting to read from address 0x999000 will read the first byte of /tmp/file.dat

  4. after the mmap call, attempting to write to address 0x999000 will modify /tmp/file.dat

  5. after the mmap call, attempting to write to address 0x99A485 will modify /tmp/file.dat

Question 3: Suppose we are using a system which uses the "second chance" replacement policy which has the following list of physical pages with the following "referenced" (or "accessed") bit values, starting with the most recently added page:

  • physical page 4 (referenced)
  • physical page 3 (referenced)
  • physical page 1 (not referenced)
  • physical page 2 (referenced)

Suppose the program accesses an unloaded virtual page A. Immediately after this, it accesses another unloaded virtual page B. To load each of these virtual pages, the OS replaces the contents of one of the physical page on its list. Which pages will be chosen?

  1. 1 for A; 1 for B (overwriting A)

  2. 1 for A; 2 for B

  3. 1 for A; 3 for B

  4. 1 for A; 4 for B

  5. 2 for A; 1 for B

  6. 2 for A; 2 for B (overwriting A)

  7. 2 for A; 3 for B

  8. 2 for A; 4 for B

  9. 4 for A; 3 for B

  10. 4 for A; 1 for B

  11. none of the above

Question 4: Suppose a program maps one page of a file from disk to its address space using mmap with MAP_SHARED and PROT_WRITE (so copy-on-write is not used and the mapping is writeable). The program first reads from the mapping, then writes to it repeatedly. What is true about writing to this mapping?
Select all that apply

  1. each write will trigger an exception or fault, so the OS can promptly handle the write

  2. the data written will be immediately available to another process that reads from the file

  3. the data written will be immediately written to the disk

Quiz 08

Question 1: When performing readahead, an operating system detects that a program accessed consecutive locations in a file. Based on this, it predicts that the program will read future locations in the file and reads this data into memory before the program is likely to access them.

Which of the following statements about readahead are true?
Select all that apply

  1. readahead is likely to be slower when the program only accesses the file data once

  2. if the operating predicts future accesses incorrectly, then it may evict more cached data that will be accessed than if it did not implement readahead

  3. to maximize performance, the OS should start reading more from the file only after the program has accessed all the pages of the file that were already read into memory

Suppose a very simple hard disk controller exposes a buffer and several control registers via fixed (physical) memory addresses:

The disk will trigger an interrupt whenever the value of the status register changes from "reading" or "writing" to "idle".

Question 2: (see above) A process running on a POSIX-like system makes read call on the device file for the hard disk controller. The hard disk's device driver's implementation of the read operation will, assuming the disk is currently idle, update the register indicating the sector number to read to, then write 1 to the control register to start a read. After this, what would make the most sense for the device driver's read implementation do next?

  1. put the current thread to sleep until it's woken up by the device driver's interrupt handler implementation

  2. update the process's page table entry for the virtual address of the user's buffer passed to the read system call to point at the device's buffer

  3. write the virtual address passed of the user's buffer passed to the read system call to the disk controller's buffer

  4. read the status register, and if it indicates the device is currently "reading", return successfully from the read system call

Question 3: (see above) If we modified this disk controller's interface to support direct memory access, the most likely change would be

  1. replacing the buffer with a register in which a device driver would place a physical memory address before doing a read or write

  2. modifying the status register's value to include the fixed memory address assigned to the buffer in addition to indicating whether the disk is reading, writing, or idle

  3. the operating system would make the processor access values in the controller's buffer with the processor cache enabled instead of disabled

Question 4: In the FAT filesystem, creating a new file A in a directory D potentially involves updating which the following?
Select all that apply

  1. the BIOS parameter block, also known as the filesystem header

  2. file allocation table entries corresponding to clusters for the file A's data

  3. file allocation table entries corresponding to clusters for the directory D's data

  4. the clusters for the file A's data

  5. the clusters for the directory D's data

Quiz 09

Question 1: Suppose a inode-based filesystem running requires 8B for each block number, uses 2048B blocks and stores 10 direct pointers in its inode, one indirect pointer, and one double-indirect pointer. How large a file can this filesystem support? (MB = 2 to the 20 bytes below)

  1. more than 4096MB

  2. between 1024MB and 4096MB

  3. between 256MB and 1024MB

  4. between 128MB and 256MB

  5. between 32MB and 128MB

  6. between 8MB and 32MB

  7. between 1MB and 8MB

  8. between 256KB and 1MB

  9. less than 256KB

Question 2: An inode-based filesystem with block groups (also known as cylinder groups) improves performance by
Select all that apply

  1. making it likely that all the inodes are stored together on disk

  2. making it likely that all the directories are stored together on disk, seperate from the regular files

  3. making it likely that an inode is stored near its directory entry on disk

  4. making it likely that the data for a file is allocated in only one region of disk

  5. allowing the number of inodes the filesystem supports to be dynamically changed

Question 3: In lecture, we discussed RAID 5, which divides data up among multiple disks, but allows failures to be tolerated by also storing "parity" information --- the result for XORing data from other disks together. Suppose we have a RAID 5 system which stores 8 sectors (A1, A2, B1, B2, C1, C2, D1, D2) where the parity information is scattered among multiple disks like as follows:

Disk 1A1B1Cp = C1 XOR C2D1
Disk 2A2Bp = B1 XOR B2C1D2
Disk 3Ap = A1 XOR A2B2C2Dp = D1 XOR D2

In this scheme with three disks as pictured, above, if all data from disk 3 is lost, recovering the value of the lost sectors B2 and C2 will reading how many sectors?

  1. 1

  2. 2

  3. 3

  4. 4

  5. 5

  6. 6

  7. 7

Question 4: Suppose an inode-based filesystem moves a file from an old to a new directory by taking the following steps in the following order:

  • overwrite the old directory's entry for the file with an "unused" entry
  • if the new directory requires more space to store the directory entry, update its inode to point to an extra data block
  • write the new directory's entry for the file
  • write to the free block map for the extra directory data block, if any
  • update the new and old directory's inode to change modification times

What are possible outcomes if the system is interrupted and unexpectedly shuts down in the middle of this process? (You may assume that each step listed above completes entirely or not at all.)
Select all that apply

  1. the file being moved is not present in either directory

  2. a directory has a block pointer that points to disk space that is marked as free

  3. a directory has a block pointer that points to disk space whose value does not contain valid directory entries

  4. the file being moved is present in both directories

  5. the file being moved has its block pointer pointing to disk space that is marked as free

Quiz 10

Question 1: Consider an xv6-like inode-based filesystem that uses redo-logging. Suppose this filesystem does not want to report a file deletion operation as complete until the deletion will persist even if the machine loses power. To delete a file, the filesystem will overwrite the directory entry for the file with one labelled as "unused", update the free block list, and update the file inode (to mark it as free) and directory inode (to change its modification time). It should return from the file deletion system call after

  1. the value in directory entry is updated

  2. the log message containing the updated directory entry is written

  3. the log message containing the file's inode is written

  4. the log message containing the directory's inode is written

  5. the log message containing the update free block map is written

  6. the log message indicating that a transaction is committed is written

  7. the log message indicating that a transaction has been started is written

  8. the log is updated to remove the transaction that deletes the file and replace it with new transactions

Question 2: When overwriting 3 blocks of data in a 10 block file on a filesystem that does copy-on-write snapshots, the filesystem will
Select all that apply

  1. allocate 10 blocks of space for a new copy of the file's data

  2. allocate a new copy of the file's inode

  3. allocate a new copy of the entire inode array

  4. allocate space for pointers to inodes

Question 3: Port numbers
Select all that apply

  1. can be set by bind() in the POSIX socket APIs

  2. are assigned only to the end of a connection which calls accept()

  3. are typically used by routers to decide where to send messages

Question 4: In POSIX sockets, a server socket
Select all that apply

  1. is created whenever a successful call to bind() is made

  2. is used for read() calls, to receive the initial requests from clients

  3. is used to create other sockets

  4. can be assigned a file descriptor

Quiz 11

Question 1: Consider two phase commit with a coordinate machine C and two worker machines A and B. Suppose after both A and B send their 'agree-to-commit' messages to C, then after C tries to send its 'global-commit' message to A and B, it discovers that B has lost power and cannot receive the 'global-commit' message. What should C do while machine B remains down?

  1. Sending a 'global-abort' message to A.

  2. Restart the transaction from the beginning by sending a new 'prepare' message to A.

  3. Allow A to commit the transaction.

  4. Forwarding B's 'agree-to-commit' message to A since B won't be able to send it.

Question 2: We described two-phase commit in terms of the worker and coordinator nodes operating according to a state machine. In order to recover properly from failures, as it transitions from one state to another, each worker machine writes ___________ to its log __________ it sends the messages (such as votes on transactions) triggered by that transition.

  1. its new state / after

  2. its new state / before

  3. its prior state / after

  4. its prior state / before

  5. the coordinator's last known state / before

  6. the coordinator's last known state / after

Question 3: Suppose one wanted to:

  • allow processes running for a list of users to write data to a file, but only if they are writing ASCII data to the file, and
  • disallow any other users (except maybe the system administrator) from modifying the file

Which of the following access control mechanisms could enforce these restrictions?
Select all that apply

  1. POSIX's file permissions (without full access control lists)

  2. POSIX access control lists

  3. a setuid program that those processes would run, which would perform the write on their behalf

  4. providing those processes with a file descriptor for the file over a local socket

Question 4: In a capability-based system, to give a process access to files in a directory, but only to read it, one would most likely

  1. start the process with the appropriate user ID and changes the metadata on the directory and the files it contains

  2. start the process with the appropriate user ID and add the user ID and the directory name to an appropriate configuration file

  3. start the process with an appropriate file descriptor open to the directory in question

  4. change the set-group-ID bit on the executlabe the process runs