COMP3000 Operating Systems F23: Tutorial 6
In this tutorial you will be learning about two implementations of the producer-consumer problem, a classic example of a concurrency problem. The class textbook covers concurrency in great detail in Chapters 25-34, and the producer-consumer problem is covered in Chapter 30 (Condition Variables) and Chapter 31 (Semaphores). While you can look at this part of the textbook, note that we will not be covering this material in the same level of detail, as should be clear from this tutorial.
A: Getting Started
Download 3000pc.zip, unpack, and run make to compile 3000pc-fifo and 3000pc-rendezvous.
Try to take this opportunity to read and understand the two programs line by line.
- Note that these programs take 3 arguments each:
- The number of events to process
- The number of events to produce before the producer sleeps for 1 second
- The number of events to consume before the consumer sleeps for 1 second
- Note that these programs take 3 arguments each:
- Run both programs with the same arguments of your choice. Do this a few times with different arguments. Do they behave the same way? Do you notice any differences? (again, no “correct” answers but just play with them and document your observations)
- Repeat the above experiment, this time running each program under strace (with the -f flag to trace children too). Do you notice any difference in the system calls each program makes? Remember to ignore the lines before main() that might be distracting, e.g., start looking around and after the clone() system call.
- You can see there is "#define QUEUESIZE 32" in both programs. Are they actually used? Why (not)?
B: Producer/Consumer with Pipes
- Examine the source code of 3000pc-fifo. Pay attention to the call to pipe(pipefd) on line 192 and make the connection with what has been discussed in the lecture (also look at the man page for pipe(2)). Explain the following:
- How does the consumer receive words to consume?
- How does the producer generate and send words to the consumer?
- Why is the call to srandom(time(NULL)) on line 169 necessary?
- Replace the call to srandom(time(NULL)) on line 169 with srandom(42). What differences do you notice in 3000pc-fifo's behavior?
- Examine the source code of 3000pc-rendezvous. Explain the following:
- What are a few ways that 3000pc-rendezvous is different from 3000pc-fifo?
- What does the call to mmap() on line 347 do (compared to our previous uses of mmap())? How did you figure this out?
- How does the producer notify the consumer that the queue is no longer empty?
- How does the consumer notify the producer that the queue is no longer full?
- What arguments can you provide to make the producer wait for the consumer? Hint: Check the size of the queue.
- What arguments can you provide to make the consumer wait for the producer? (You can disregard the wait at the very beginning as the queue is initially empty)
- Another student tells you that the difference between processes and threads is that processes never share memory, while threads do. Is this statement correct or incorrect? How can the behavior of 3000pc-rendezvous help justify your answer?
- Change the calls to pthread_mutexattr_setpshared() and pthread_condattr_setpshared() (lines 293 and 298) to take 0 instead of 1. How does the behavior or 3000pc-rendezvous change? Does anything break?