Overview: A consumer/producer problem is very similar to a reader/writer problem. In our program we will have one producer and one consumer. 1. Get the lab sample files into your directory. 2. Copy echo.c to your mylab6.c 3. Add mylab6.c to your Makefile. 4. Build and run the program. 5. Move the producer functionality to a function outside of main. This will run as a thread. 6. Build a producer function of your own design. It will be very similar to the consumer function. It will run as a thread. 7. Start the producer and consumer threads running. 8. Add a set of two System-V semaphores to your program. Use your semaphores to synchronize the producing & consuming. 9. Procedure: Each thread will wait for its own semaphore to be available. A wait-for-zero operation will be implemented. The critical code will be executed. Then the other thread's semaphore will be released. A sample of using multiple semaphores in a program can be seen in our own 3600/5/thread_ex.c program written in class. Odin files to be collected from 3600/6 folder: mylab6.c Makefile foo poem
Copy all example code for this week into your directory:
$ cd $ cd 3600/6 $ cp /home/fac/gordon/public_html/3600/examples/6/* .Execute the programs and verify that they are working as you would expect. Review the source of each program. You should understand the concepts before starting to code the lab. Your job is to implement producer/consumer problem synchronization for the two threads in echo.c. Linux supports POSIX interface for semaphores, which is considerably more user-friendly than System V, but System V is the de facto standard - most system code uses System V. You will add SysV semaphores to echo.c. Copy echo.c to mylab6.c:
$ cp echo.c mylab6.c
Add an entry for mylab6.c to your Makefile.
Program echo.c is a bounded buffer problem with a buffer size of one character.producer thread pid: 27346 tid: 27346 consumer thread pid: 27346 tid: 27347 he ssuuunnn iiinnn mmy hhaanndd GGGooonnnnnnaa tSTEP 1. In echo.c the main thread is also the producer thread. Modify this so that the producer is its own thread with its own thread function. The main thread will create both the producer and the consumer threads. Make sure your code is working with the new thread before continuing. Join your producer thread in main.
STEP 2. Now you will solve the problem of synchronization. Create two SysV semaphores. Use these semaphores to synchronize the two threads so that the producer does not modify the char buffer until the consumer has consumed the char (read the value) and the consumer does not consume the same value twice (e.g., consume from an empty/old buffer). If all goes correctly you should see the poem displayed correctly on the screen. During debugging it is useful to grab information about a semaphore. For help on doing this see sem_ctrl.c
• Do not define multiple semids.
Define just one semid, but then specify a set of semaphores in semget().
In your grab[] and release[] arrays, indicate a sem_num of 0 or 1 for the
semaphore you are using.
Unlike the Producer/Consumer solution in lab-5 that used a single semaphore, use two semaphores for this lab. One semaphore will control the producer and one semaphore will control the consumer. The advantage to two semaphores is that the operations to grab and release are similar but the semaphore that the operation is applied to alternates. The producer and consumer execute their critical section the same number of times inside a loop. To prevent deadlock, both threads will release the other's semaphore before exit. You can ensure that the producer starts first by initializing the producer's semaphore value appropriately.
This section updated to reflect the numbers used during lectures. Each thread has a loop. Inside the loops you will grab, then release a semaphore. Below is thread pseudocode. *------------* *------------* * PRODUCER * * CONSUMER * *------------* *------------* # producer starts # consumer blocks PSEM.val = 0 CSEM.val = 1 # start looping # start looping top: top: if DONE if DONE goto end goto end # grab producer sem # grab consumer sem wait_on_zero wait_on_zero PSEM.op += 1 CSEM.op += 1 {{ critical section }} {{ critical section }} # release consumer sem # release producer sem CSEM.op -= 1 PSEM.op -= 1 goto top goto top end: end:
STEP 3. Pass the value for LIMIT in from the the command line. Set LIMIT to a default value of 50 if no argument is passed.
Next, open a logfile named log. Do this in the main thread before starting the threads. The consumer will write to log. Use formatted file streams to make coding a little easier. Formatted file streams: fopen, fprintf, fscanf, fclose.
FILE *fout; /* needs to be global so threads can see it */ fout = fopen("log", "w"); if (fout == NULL) { fprintf(stderr, "error opening output file.\n"); exit(1); }
All output from the consumer should go to log instead of to the screen. Close the file after the main thread joins the two threads.
Sample runs...$ ./lab6 10 $ cat log consumer thread pid: 26878 tid: 26879 With the s$ ./lab6 63 $ cat log consumer thread pid: 26878 tid: 26879 With the sun in my hand Gonna throw the sun Way across the landSample run with strace...$ strace -e trace=ipc,clone ./lab6 136 semget(0xc02a2de, 2, IPC_CREAT|0666) = 397180933 semctl(397180933, 0, SETVAL, 0x7ffe00000001) = 0 semctl(397180933, 1, SETVAL, 0x7ffe00000000) = 0 clone(child_stack=0x7ffe20abcff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES| CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID| CLONE_CHILD_CLEARTID, parent_tidptr=0x7ffe20abd9d0, tls=0x7ffe20abd700, child_tidptr=0x7ffe20abd9d0) = 10153 clone(child_stack=0x7ffe202bbff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES| CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID| CLONE_CHILD_CLEARTID, parent_tidptr=0x7ffe202bc9d0, tls=0x7ffe202bc700, child_tidptr=0x7ffe202bc9d0) = 10154 semctl(397180933, 0, IPC_RMID, 0x202c0) = 0 $ cat log consumer thread pid: 26878 tid: 26879 With the sun in my hand Gonna throw the sun Way across the land- Cause I'm tired, Tired as I can be So Tired Blues - by Langston HughesOdin files to be collected. 3600/6/mylab6.c 3600/6/Makefile 3600/6/foo 3600/6/poem