CS 332 w22 — Lab 3 FAQ

Table of Contents

pipe-diagram.png

1 Pipes and synchronization

  • A reader-writer lock is probably a more complex solution than is necessary
    • Having lots of readers would be very unusual (though technically possible)
  • A pipe is essentially a bounded buffer/producer-consumer, so it will need
    • a mutex (lock) to ensure only one thread is reading or writing at a time
    • a condition variable for consumers/readers to wait on
    • a condition variable for producers/writers to wait on
  • Alternatively, you could create a bounded buffer as a separate type of data structure in the kernel (where a pipe would have a struct BBQ inside it)
    • In this case, the struct BBQ would have a lock and two condition variables, though you likely would still need a separate lock for the pipe to protect the pipe struct's other fields

2 dup and pipes

  • There's nothing special about dup here (the lab writeup is unnecessarily specific on this point, the sentence has been removed)
  • If a process opens a pipe and forks many times, many processes could be reading or writing to the pipe
  • Thus, pipe_read and pipe_write need to be thread-safe

3 Can a process read while another writes?

  • One process can certainly call pipe_read while another calls pipe_write
  • You will need to use a lock and condition variables to ensure correct behavior under concurrency

4 static

  • In C, a static function or variable is visible only in the file where it is declared
  • So it's appropriate for functions like pipe_read and variables like pipe_ops that should only be used as part of the pipe implementation and nowhere else in the kernel
  • A pipe.h file would only be needed to declare non-static functions/variables that need to be used in multiple files
    • For example, you might implement a pipe_init function in pipe.c that is used in sys_pipe in syscall.c

5 f_ops

The pipe_read, pipe_write, and pipe_close functions you are implementing are the ones f_ops->read, f_ops->write, and f_ops->close will point to for pipe files. Inside pipe_read there's no other function to call to perform the read for you—you are implementing that function.

6 pipe_close

  • Free the pipe struct/bounded buffer once both ends of the pipe are closed
    • Remember from 208: every call to malloc should have a corresponding call to free
    • So if you kmalloc or kmem_cache_alloc memory for the pipe struct, you will also need to kfree or kmem_cache_free that memory
  • Take a look at the code for fs_close_file:
void
fs_close_file(struct file *file)
{
    sleeplock_acquire(&file->f_lock);
    file->f_ref--;
    if (file->f_ref > 0 || file == &stdin || file == &stdout) {
        sleeplock_release(&file->f_lock);
        return;
    }
    sleeplock_release(&file->f_lock);
    // guaranteed that we are the last reference to this file
    if (file->f_inode) {
        fs_release_inode(file->f_inode);
    }
    if (file->f_ops->close) {
        file->f_ops->close(file);
    }
    fs_free_file(file);
}
  • If there are processes that still have the file open (i.e., file->f_ref > 0), then it just returns after decrementing this reference count
  • If no processes are still using the file, that's when it will call file->f_ops->close(file) (i.e., pipe_close), and afterwards free the file struct (fs_free_file)
  • This means your pipe_close will only be called if all processes that had a pointer to that end of the pipe have closed the file

7 Where do we save the excess if the buffer we read contains more bytes than the user requested?

You should only read as many bytes from the buffer as the user requested (read them one at a time in a loop, copying the byte from the pipe's data array to the array (buf) the user passed in

8 If the user makes a request after the read descriptor is closed, where do we retrieve the data from?

The only way for the user to make a request is with the descriptor for the read end of the pipe. If the user has previously closed that file, that entry in the open file table should be NULL, and sys_read will return an error.

9 How do we handle a write waiting to insert in a full pipe,we need to make sure that the writes are done atomically and with the default setup it could be the case that a write waits once the buffer is full and then another write gets the lock?

The pipe is not required to allow waiting writes to proceed in first-come-first-serve order, so it would be valid behavior for the second write to go first. The user would be responsible for accounting for this concurrent write behavior.

10 What do we do with the offset variable?

You do not need to do anything with it—it's used by the file system, but it's necessary for pipe. It has to be an argument to pipe_read and pipe_write in order to conform to the file API.