Operating Systems


Homework 4

[total 26 marks]

Hand-In Procedure

You are to turn in this homework before the lecture begins. Please drop your homework in a cardboard box near to the lecture podium. Your homework must be hand-written (typed homeworks will not be accepted).

Assignment : Spin-locking

[4 marks]

Read: spinlock.c In this part of the assignment we will explore some of the interaction between interrupts and locking.

Make sure you understand what would happen if the kernel executed the following code snippet:

  struct spinlock lk;
  initlock(&lk, "test lock");
  acquire(&lk);
  acquire(&lk);
(Feel free to use QEMU to find out. acquire is in spinlock.c.)

An acquire ensures interrupts are off on the local processor using the cli instruction (via pushcli()), and interrupts remain off until the release of the last lock held by that processor (at which point they are enabled using sti).

Let's see what happens if we turn on interrupts while holding the ide lock. In iderw in ide.c, add a call to sti() after the acquire(), and a call to cli() just before the release(). Rebuild the kernel and boot it in QEMU. Chances are the kernel will panic soon after boot; try booting QEMU a few times if it doesn't.

Turn in: explain in a few sentences why the kernel panicked. You may find it useful to look up the stack trace (the sequence of %eip values printed by panic) in the kernel.asm listing.

Remove the sti() and cli() you added, rebuild the kernel, and make sure it works again.

Now let's see what happens if we turn on interrupts while holding the file_table_lock. This lock protects the table of file descriptors, which the kernel modifies when an application opens or closes a file. In filealloc() in file.c, add a call to sti() after the call to acquire(), and a cli() just before each of the release()es. You will also need to add #include "x86.h" at the top of the file after the other #include lines. Rebuild the kernel and boot it in QEMU. It will not panic.

Turn in: explain in a few sentences why the kernel didn't panic. Why do file_table_lock and ide_lock have different behavior in this respect?

You do not need to understand anything about the details of the IDE hardware to answer this question, but you may find it helpful to look at which functions acquire each lock, and then at when those functions get called.

(There is a very small but non-zero chance that the kernel will panic with the extra sti() in filealloc(). If the kernel does panic, make doubly sure that you removed the sti() call from iderw. If it continues to panic and the only extra sti() is in filealloc(), then email the staff and think about buying a lottery ticket.)

Turn in: Why does release() clear lk->pcs[0] and lk->cpu before clearing lk->locked? Why not wait until after?

Uniprocessor Locking

A uniprocessor OS does not require atomic instructions like xchg to implement locks (or other forms of synchronization). The concurrency on uni-processor systems is due to pre-emption of threads by the timer interrupt. Because the timer interrupt may pre-empt a thread at any point in program execution, instructions of two threads may interleave in any arbitrary order.

Synchronization primitives that work on multi-processor systems are guaranteed to work on uni-processor systems (reason it for yourself). On uniprocessor systems, it is also possible to implement critical sections by disabling interrupts.

Here are some (possibly incorrect) implementations of lock() and unlock() primitives using interrupt enable and disable mechanisms. Answer the associated questions.
lock(L) {
  cli();   //disable preemption
  while (L==0) continue;
  L = 0;
  sti();   //enable preemption
}

unlock(L) {
  L = 1;
}
Does this implementation of locks work on a uniprocessor? If not, why not?
lock(L) {
  int acquired = 0;
  while (!acquired) {
    cli();
    if (L == 1) {
        acquired = 1;
        L = 0;
    }
    sti();
   }
}

unlock(L) {
  L = 1;
}
Does this implementation of locks work on a uniprocessor? If not, why not?

Assignment : sleep and wakeup

[2 marks]

Here is a version of the code for a single-queue, multiple-consumer, mutiple-producer problem.

struct pcq {
    void *ptr;
    struct spinlock lock;
};

void*
pcqread(struct pcq *q)
{
    void *p;

    acquire(&q->lock);
    while(q->ptr == 0)
        sleep(q, &q->lock);
    p = q->ptr;
    q->ptr = 0;
    wakeup(q);  /* wake pcqwrite */
    release(&q->lock);
    return p;
}

void
pcqwrite(struct pcq *q, void *p)
{
    acquire(&q->lock);
    while(q->ptr != 0)
        sleep(q, &q->lock);
    q->ptr = p;
    wakeup(q);  /* wake pcqread */
    release(&q->lock);
    return p;
}

Turn in: Both producer (pcqwrite) and consumer (pcqread) are sleeping on the same channel q. Is this correct? Why or why not? Should they sleep on different channels? For example, what happens if the producer calls wakeup(q)? Can some unrelated part of the code call wakeup a consumer thread?

Assignment : xv6 file system

[8 marks]

Read: sysfile.c (create(), sys_unlink()), fs.c (readi(), writei(), dirlink(), ialloc(), iupdate(), iget(), ilock(), iunlock(), iput(), itrunc()), bio.c (bget(), bread(), bwrite(), brelse())

Add the following line at the beginning of the log_write() function in log.c

cprintf("log_write %d\n", b->sector);
This will record all writes to the file system along with the sector number.

Start a new session on xv6 with a fresh disk (using make clean followed by make qemu, and type the following command:

$ echo > a
This command creates a new file. You will see a series of disk writes (printed in log_write()).

Turn in: Report the printed output and explain what is being written in each disk write. What is the third disk write (to sector 29)? You may want to insert cprintf() statements in xv6 code to see where the writes are coming from.

Interrupt the previous command (leave the newly created file unchanged). Next, execute the following command to write data to this file:

$ echo x > a

Turn in: Report the printed output and explain what is being written in each disk write. Why do you see writes to block 4 (and to 426) twice? You may want to insert cprintf() statements in xv6 code to see where the writes are coming from.

Next, delete the file by typing the following command:

$ rm a

Turn in: Report the printed output and explain what is being written in each disk write.

Assignment : ZCAV

[12 marks]

This homework is based on the ZCAV tool: see ZCAV documentation here.

Background

Given constant rotational speed (constant angular velocity, CAV) of a magnetic disk, the head can cover larger distance on the outer tracks in the same time, compared to the inner tracks. If the sector density on all tracks is same, this would cause space wastage on the outer tracks (as their density would be limited by the inner tracks).

To deal with this situation, modern disks divide their tracks into zones. The inner zones have lower angular sector density, while the outer zones have higher angular sector density. Effectively, this means that the disk throughput at the outer zones is higher. This is also called zoned constant angular velocity (ZCAV).

To take advantage of this, the disk controllers usually map the lower sector numbers (starting from sector number zero) on the outer tracks, while the higher sector numbers are mapped to inner tracks. The thinking being that the lower sector numbers are likely to have higher frequency of access. Notice that this is just an assumption and may not be true!

ZCAV program measures the disk throughput at different sector numbers. To do this, it reads/writes 256MB (default) worth of data starting at sector X, where X is varied from 0 to disk-size.

You can see the results of running ZCAV on some disk configurations here.

Turn in

Run ZCAV using the following command for the following types of disks: Report all results as graphs as at this URL. Feel free to use the GNUPlot scripts available at this URL (e.g., command.gnuplot.zcav). In particular, how many zones do you find? What is the mapping between sector numbers and physical disk layout? What are the corresponding disk bandwidths? Anything else you notice? Finally, explain the different behaviour in different environments.

Tips

Fun

This part is optional. If you do these, feel free to submit your results.