Semaphores for Solving Coordination Problems
Involving Multiple Processes |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
#include <fcntl.h> // For O_CREAT #include <semaphore.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { sem_t *completed_a1, *completed_b1; // Call open() in the original process so that we don't have to // worry about coordinating the call to open() completed_a1 = sem_open("/completed_a1", O_CREAT, O_RDWR, 0); completed_b1 = sem_open("/completed_b1", O_CREAT, O_RDWR, 0); pid_t pid = fork(); switch(pid) { case -1: exit(1); case 0: // Child write(STDOUT_FILENO, "Task A1\n", 8); sem_post(completed_a1); // Increment the semaphore sem_wait(completed_b1); // Try to decrement the semaphore write(STDOUT_FILENO, "Task A2\n", 8); sem_close(completed_a1); sem_close(completed_b1); sem_unlink("/completed_a1"); exit(0); default: // Parent write(STDOUT_FILENO, "Task B1\n", 8); sem_post(completed_b1); // Increment the semaphore sem_wait(completed_a1); // Try to decrement the semaphore write(STDOUT_FILENO, "Task B2\n", 8); sem_close(completed_a1); sem_close(completed_b1); sem_unlink("/completed_b1"); write(STDOUT_FILENO, "\nCheck: ls -l /dev/shm/sem.* \n", 30); exit(0); } }
#include <fcntl.h> // For O_CREAT #include <semaphore.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(void) { sem_t *completed_a1, *completed_b1; // Call open() in the original process so that we don't have to // worry about coordinating the call to open() completed_a1 = sem_open("/completed_a1", O_CREAT, O_RDWR, 0); completed_b1 = sem_open("/completed_b1", O_CREAT, O_RDWR, 0); pid_t pid = fork(); switch(pid) { case -1: exit(1); case 0: // Child write(STDOUT_FILENO, "Task A1\n", 8); sem_wait(completed_b1); // Try to decrement the semaphore sem_post(completed_a1); // Increment the semaphore write(STDOUT_FILENO, "Task A2\n", 8); sem_close(completed_a1); sem_close(completed_b1); sem_unlink("/completed_a1"); exit(0); default: // Parent write(STDOUT_FILENO, "Task B1\n", 8); sem_post(completed_b1); // Increment the semaphore sem_wait(completed_a1); // Try to decrement the semaphore write(STDOUT_FILENO, "Task B2\n", 8); sem_close(completed_a1); sem_close(completed_b1); sem_unlink("/completed_b1"); write(STDOUT_FILENO, "\nCheck: ls -l /dev/shm/sem.* \n", 30); exit(0); } }
Note: With this protocol B1 will always be completed before A1 (which is not required).
Producers
product = produce(); add(product, warehouse);
Consumers
product = get_next(warehouse); use(product);
Semaphores
sem_t *key = sem_open("/key", O_CREAT, ORDWR, 1); sem_t *available = sem_open("/available", O_CREAT, ORDWR, 0);
Producers
product = produce(); sem_wait(key); // Wait for the key to the warehouse add(product, warehouse); sem_post(key); // Release the key to the warehouse sem_post(available); // Indicate that a product is available
Consumers
sem_wait(available); // Wait until a product is available sem_wait(key); // Wait until the key to the warehouse is available product = get_next(warehouse); sem_post(key); // Release the key to the warehouse use(product);
sem_wait(available)
and sem_wait(key)
, another process gets access
to the warehouse and there is only a single product in the
warehouse?
available
The Better Producer Protocol
product = produce(); sem_wait(key); // Wait until the key is available add(product, warehouse); sem_post(key); // Release the key sem_post(available); // Indicate that a product is available
The Worse Producer Protocol
product = produce(); sem_wait(key); // Wait until the key is available add(product, warehouse); sem_post(available); // Indicate that a product is available sem_post(key); // Release the key
Note: This protocol is worse because a consumer may stop waiting on available product before it can get the key (i.e., be unblocked and then immediately blocked again).
The Correct Consumer Protocol
sem_wait(available); // Wait until a product is available sem_wait(key); // Wait until the key is available product = get_next(warehouse); sem_post(key); // Release the key use(product);
A Consumer Protocol that is not Deadlock-Free
sem_wait(key); // Wait until the key is available sem_wait(available); // Wait until a product is available product = get_next(warehouse); sem_post(key); // Release the key to the warehouse use(product);
A consumer can get the key when no product is available and, since the key is no longer available, no producer will be able to add one.
Semaphores and Shared Variables
sem_t *updateable = sem_open("/updateable", O_CREAT, ORDWR, 1); sem_t *key = sem_open("/key", O_CREAT, ORDWR, 1); int readers = 0;
Writers
sem_wait(key); // Wait for the key // Critical section for writers sem_post(key); // Release the key
Readers
sem_wait(updateable); // Wait until reader info can be updated readers++; if (readers == 1) sem_wait(key); // First in gets the key sem_post(updateable); // Allow other readers to update info // Critical section for readers sem_wait(updateable); // Wait until the reader info can be updated readers--; if (readers == 0) sem_post(key); // Last out releases the key sem_post(updateable); // Allow other readers to update info