CS 332 w22 — Lab 3 FAQ
Table of Contents
- 1. Pipes and synchronization
- 2.
dup
and pipes - 3. Can a process read while another writes?
- 4.
static
- 5.
f_ops
- 6.
pipe_close
- 7. Where do we save the excess if the buffer we read contains more bytes than the user requested?
- 8. If the user makes a request after the read descriptor is closed, where do we retrieve the data from?
- 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?
- 10. What do we do with the offset variable?
pipe_read
will be similar tobbq_remove
pipe_write
will be similar tobbq_insert
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
- In this case, the
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
andpipe_write
need to be thread-safe
3 Can a process read while another writes?
- One process can certainly call
pipe_read
while another callspipe_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 likepipe_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 inpipe.c
that is used insys_pipe
insyscall.c
- For example, you might implement a
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
orkmem_cache_alloc
memory for the pipe struct, you will also need tokfree
orkmem_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.