160 likes | 229 Views
POSIX SEMAPHORES. Mutex Reminder. mutex is a lock that only one thread can "hold" at any given time If a thread tries to acquire the lock while another thread "holds" it, that thread will be blocked until the lock is released
E N D
Mutex Reminder • mutex is a lock that only one thread can "hold" at any given time • If a thread tries to acquire the lock while another thread "holds" it, that thread will be blocked until the lock is released • This mechanism is quite helpful when you want only one thread executing certain code at a given time
Introducing Semaphores • A semaphore is a counter that can be used to keep track of resources in threaded applications. • It basically represents a non-negative value that can be signaled or waited on • The value of the semaphore can be thought of as the number of unused resources • When this number reaches zero, a thread that wishes to use a resource must wait until one is available.
Simple Semaphore Implementation struct semaphore { int val; thread_list waiting; // List of threads waiting for semaphore } wait(semaphore Sem): // Wait until > 0 then decrement while (Sem.val <= 0) { add this thread to Sem.waiting; block(this thread); } Sem.val = Sem.val -1; return; post(semaphore Sem): // Increment value and wake up next thread Sem.val = Sem.val + 1; if (Sem.waiting is nonempty) { remove a thread T from Sem.waiting; wakeup(T); }
Why to use semaphores? • Semaphores impose deliberate constraints that help programmers avoid errors. • Solutions using semaphores are often clean and organized, making it easy to demonstrate their correctness. • Semaphores can be implemented efficiently on many systems, so solutions that use semaphores are portable and usually efficient.
Semaphore Implementation • How do we ensure that the semaphore implementation is atomic? • One approach: Make them system calls, and ensure only one P() or V() operation can be executed by any process at a time. • This effectively puts a lock around the P() and V() operations themselves! • Easy to do by disabling interrupts in the P() and V() calls. • Another approach: Use hardware support • Say your CPU had atomic P and V instructions • That would be sweet.
The Producer - Consumer Problem • Producer pushes items into the buffer. • Consumer pulls items from the buffer. • Producer needs to wait when buffer is full. • Consumer needs to wait when the buffer is empty.
Consumer() { int item; while (TRUE) { P(full); P(mutex); item = remove_item(); V(mutex); V(empty); eat(item); } } Semaphore mutex = 1;Semaphore empty = N;Semaphore full = 0; • Producer() { int item; while (TRUE) { item = bake(); P(empty); P(mutex); insert_item(item); V(mutex); V(full); } }
Create a semaphore • int sem_init (sem_t* sem, int pshared, unsigned int value); • sem_t - the semaphore to be initialized • pshared - indicates whether this semaphore is to be shared between the threads of a process, or between processes • value - the initial value of the semaphore • All semaphore functions return zero on success and non-zero on failure.
Semaphore Operations • int sem_post(sem_t * sem); • This will increase the value of the semaphore by one. • int sem_wait (sem_t* sem); • This will return immediately if the value of the semaphore is greater than zero and block the thread otherwise. It decreases the semaphore by one.
Semaphore Operations • int sem_trywait(sem_t * sem); • Version of the sem_wait which not blocks. Decreases the semaphore by one if the semaphore does not equal to zero. If it is zero it does not block, returns zero with error code EAGAIN • int sem_destroy(sem_t * sem); • It releases the resources that semaphore has and destroys it. • int sem_getvalue(sem_t * sem, int * semdeg); • The current semaphore value is stored in semdeg variable.