From CS61
Jump to: navigation, search

11/3: Weensy Pipes

This exercise aims to teach you about one of the most important and powerful abstractions provided by modern operating system kernels, the pipe. You’ll implement a pipe inside a weensy kernel.

Check out the shell1x directory of your cs61-exercises repository (you’ll need to pull first). This weensy kernel runs two processes, p-pipewriter and p-pipereader, with a “pipe” between them. The pipewriter repeatedly writes a message into the pipe, prints it, then waits for 1–3 seconds. The pipereader repeatedly reads a line from the pipe and prints it.

Currently, though, the weensy pipe is very expensive. In one run it took 13,844 system calls to write the message “I am a plumber.”! Your goal in this exercise is to make the weensy OS pipe more efficient: it should take fewer system calls to write and fewer system calls to read.

Part 0

Look at the code and understand how it works.

Note: As in the memory-mapped weensy exercise, this weensy OS has kernel isolation (the kernel’s memory is protected from processes by the page table), but not full process isolation (all processes share the same page table, kernel_pagetable).

Test your understanding: Answer Part 0’s survey questions!

In the rest of this exercise, you’ll reduce the overhead of the weensy pipe using different techniques.

Part 1: Scheduling

First, change how the WeensyOS kernel schedules its processes. Specifically, if a system call made by process P can’t do useful work until some other process runs, then the kernel should probably not run P again (it should run some other process).

Using this simple technique, you can reduce the number of system calls made by p-pipewriter to close to the minimum (say, no more than 4x the minimum).

Answer Part 1’s survey questions!

Part 2: Buffer size

Second, change the size of the WeensyOS kernel’s pipe buffer, and change the system call handlers in exception accordingly.

Using this simple technique, you can reduce the number of system calls made by p-pipewriter to 1 per message. But be careful: your code should handle all cases (e.g., writing into a partially-full buffer, writing data larger than the buffer, reading N characters from a buffer containing M characters where M > N, etc.).

Answer Part 2’s survey questions!

Part 3: Blocking

Finally, change the WeensyOS kernel so that a reader blocks when it tries to read from an empty pipe, rather than polling from the empty pipe. (A process is blocked if it is not scheduled until some condition changes. This means it doesn’t occupy the CPU, leaving more time for other processes—and wasting less energy.) The reader should be woken up only when the buffer has characters to read. Use process state P_BLOCKED to represent “blocked on reading pipe.”

Using this simple technique, you can reduce the number of system calls made by p-pipereader to roughly 2 per message!

Answer Part 3’s survey questions!

Optional advanced work

This exercise touches on several big ideas in operating systems design, with respect to both performance (scheduling, blocking vs. polling) and convenience (the pipe abstraction). Here’s how to go further.

  • Make the writer block if the pipe buffer fills up. It should unblock when there is space in the buffer. You’ll need to change the process code to check your work.
  • Cut p-pipereader down to exactly one system call per message, rather than two. (How? The core idea is that the kernel should actually perform the piperead system call when waking up a blocked reader.)
  • The easiest way to write a pipe buffer involves memmove calls to shift memory around when data is read. This can be expensive. Change your pipe buffer so no memmoves are involved. You’ll need to add at least one global variable.
  • Implement multiple pipes!

Solution video