Semaphores 7 1 Overview Binary Semaphores and Synchronization
Semaphores 7. 1 Overview Binary Semaphores and Synchronization Mutual Exclusion ® 7 -2
Overview l A Semaphore is a kernel primitive object. Semaphore operations: – Can change a task’s state. – Are fast. l Three semaphore types available: – Binary semaphores allow a task to pend until a given event occurs (e. g. , an interrupt). – Mutual exclusion semaphores allow a task to acquire an exclusive lock on a shared resource (e. g. , a file or a device). – Counting semaphores are available: Less commonly used. See manual pages for details. ® 7 -3
Semaphores Overview 7. 2 Binary Semaphores and Synchronization Mutual Exclusion ® 7 -4
The Synchronization Problem my. Get. Data ( ) { request. Data( ); Task wait. For. Data( ); get. Data( ); } l Task may need to wait for an event to occur. l Busy waiting (i. e. , polling) is inefficient. l Pending until the event occurs is better. ® 7 -5
The Synchronization Solution l Create a binary semaphore for the event. l Binary semaphores exist in one of two states: – Full (event has occurred). – Empty (event has not occurred). l Task waiting for the event calls sem. Take( ) and blocks sem. Take until semaphore is given. l Task or interrupt service routine detecting the event calls sem. Give( ), which unblocks the waiting task. sem. Give ® 7 -6
Binary Semaphores SEM_ID sem. BCreate (options, initial. State) options Specify queue type (SEM_Q_PRIORITY or SEM_Q_FIFO) for tasks pended on this semaphore. initial. State Initialize semaphore to be available (SEM_FULL) or unavailable (SEM_EMPTY). l Semaphores used for synchronization are typically initialized to SEM_EMPTY (event has not occurred). l Returns a SEM_ID, or NULL on error. ® 7 -7
Taking a Semaphore STATUS sem. Take (sem. Id, timeout) l sem. Id The SEM_ID returned from sem. BCreate( ). timeout Maximum time to wait for semaphore. Value can be clock ticks, WAIT_FOREVER, or NO_WAIT. Can pend the task until either – Semaphore is given or – Timeout expires. l Semaphore left unavailable. l Returns OK if successful, ERROR on timeout (or invalid OK ERROR sem. Id). ® 7 -8
Taking a Binary Semaphore ® 7 -9
Giving a Semaphore STATUS sem. Give (sem. Id) l Unblocks a task waiting for sem. Id. l If no task is waiting, makes sem. Id available. l Returns OK, or ERROR if sem. Id is invalid. OK ERROR ® 7 -10
Giving a Binary Semaphore ® @ 7 -11
Information Leakage l Fast event occurrences can cause information loss. l Suppose a Vx. Works task (priority=100) is executing the following code, with sem. Id initially unavailable: FOREVER { sem. Take (sem. Id, WAIT_FOREVER); printf (“Got the semaphoren”); } What would happen in the scenarios below? 1. -> repeat(1, sem. Give, sem. Id) 2. -> repeat(2, sem. Give, sem. Id) 3. -> repeat(3, sem. Give, sem. Id) ® 7 -12
Synchronizing Multiple Tasks STATUS sem. Flush (sem. Id) l Unblocks all tasks waiting for semaphore. l Does not affect the state of a semaphore. l Useful for synchronizing actions of multiple tasks. ® 7 -13
Semaphores Overview Binary Semaphores and Synchronization 7. 3 Mutual Exclusion ® 7 -14
Mutual Exclusion Problem l Some resources may be left inconsistent if accessed by more than one task simultaneously. – Shared data structures – Shared files – Shared hardware devices l Must obtain exclusive access to such a resource before using it. l If exclusive access is not obtained, then the order in which tasks execute affects correctness. – We say a race condition exists. – Very difficult to detect during testing. l Mutual exclusion cannot be compromised by priority. ® 7 -15
Race Condition Example ® 7 -16
Solution Overview l Create a mutual exclusion semaphore to guard the resource. l Call sem. Take( ) before accessing the resource; call sem. Take sem. Give( ) when done. sem. Give – sem. Take( ) will block until the semaphore (and hence the resource) becomes available. – sem. Give( ) releases the semaphore (and hence access to the resource). ® 7 -17
Creating Mutual Exclusion Semaphores SEM_ID sem. MCreate (options) l options can be: queue specification SEM_Q_FIFO or SEM_Q_PRIORITY l deletion safety SEM_DELETE_SAFE priority inheritance SEM_INVERSION_SAFE Initial state of semaphore is available ® 7 -18
Mutex Ownership l A task which takes a mutex semaphore “owns” it, so that no other task can give this semaphore. l Mutex semaphores can be taken recursively. – The task which owns the semaphore may take it more than once. – Must be given same number of times as taken before it will be released. l Mutual exclusion semaphores cannot be used in an interrupt service routine. ® 7 -19
Taking a Mutex Semaphore ® 7 -20
Giving a Mutex Semaphore ® 7 -21
Code Example - Solution 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include “vx. Works. h” #include “sem. Lib. h” LOCAL char my. Buf[BUF_SIZE]; /* Store data here */ LOCAL int my. Buf. Index = -1; /* Index of last data */ LOCAL SEM_ID my. Sem. Id; void my. Buf. Init( ) { my. Sem. Id = sem. MCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE); } void my. Buf. Put (char ch) { sem. Take(my. Sem. Id, WAIT_FOREVER); my. Buf. Index++; my. Buf[my. Buf. Index]=ch; sem. Give(my. Sem. Id); } ® 7 -22
Deletion Safety l Deleting a task which owns a semaphore can be catastrophic. – data structures left inconsistent. – semaphore left permanently unavailable. l The deletion safety option prevents a task from being deleted while it owns the semaphore. l Enabled for mutex semaphores by specifying the SEM_DELETE_SAFE option during sem. MCreate( ). ® 7 -23
Unbounded Priority Inversion ® 7 -24
Priority Inheritance l Priority inheritance algorithm solves the unbounded priority inversion problem. l Task owning a mutex semaphore is elevated to priority of highest priority task waiting for that semaphore. l Enabled on mutex semaphores by specifying the SEM_INVERSION_SAFE option during sem. MCreate( ). l Must also specify SEM_Q_PRIORITY (SEM_Q_FIFO is incompatible with SEM_INVERSION_SAFE). ® 7 -25
Priority Inversion Safety ® 7 -26
Avoiding Mistakes l It is easy to misuse mutex semaphores, since you must protect all accesses to the resource. all l To prevent such a mistake: – Write a library of routines to access the resource. – Initialization routine creates the semaphore. – Routines in this library obtain exclusive access by calling sem. Give( ) and sem. Take( ). – All uses of the resource are through this library. ® 7 -27
Caveat - Deadlocks l A deadlock is a race condition associated with the taking of multiple shared resources. l May be very difficult to detect during testing. ® 7 -28
Other Caveats l Mutual exclusion semaphores can not be used at interrupt time. This issue will be discussed later in the chapter. l Keep the critical region (code between sem. Take( ) and sem. Give( )) short. ® 7 -29
Common Routines l Additional semaphore routines: sem. Delete( ) Destroy the semaphore. sem. Take() calls for all tasks pended on the semaphore return ERROR. show( ) Display semaphore information. ® 7 -30
Semaphore Browser To inspect the properties of a specific semaphore, insert the semaphore ID in the Browser’s Show box, Show and click on Show ® 7 -31
Locking Out Preemption l When doing something quick frequently, it may be preferable to disable preemption instead of taking a mutex. l Call task. Lock( ) to disable preemption. task. Lock l Call task. Unlock( ) to reenable preemption. task. Unlock l Does not disable interrupts. not l If the task blocks, preemption is reenabled. When the task continues executing, preemption is again locked. l Prevents all other tasks from running, not just the tasks contending for the resource. ® 7 -32
ISR’s and Mutual Exclusion l ISR’s can’t use mutex semaphores. l Task sharing a resource with an ISR may need to disable interrupts. l To disable/re-enable interrupts: int. Lock( ) void int. Unlock (lock. Key) lock. Key is return value from int. Lock( ). int. Lock l Keep interrupt lock-out time short (e. g. , long enough to set a flag)! l Making kernel calls at task level can reenable interrupts! ® 7 -33
Summary Binary Mutual Exclusion sem. BCreate sem. MCreate sem. Take, sem. Give show sem. Delete ® 7 -34
Summary l Binary Semaphores allow tasks to pend until some event occurs. – Create a binary semaphore for the given event. – Tasks waiting for the event blocks on a sem. Take( ). – Task or ISR detecting the event calls sem. Give( ) or sem. Flush( ). l Caveat: if the event repeats too quickly, information may be lost. ® 7 -35
Summary l Mutual Exclusion Semaphores are appropriate for obtaining exclusive access to a resource. – Create a mutual exclusion semaphore to guard the resource. – Before accessing the resource, call sem. Take( ). – To release the resource, call sem. Give( ). l Mutex semaphores have owners. l Caveats: – Keep critical regions short. – Make all accesses to the resource through a library of routines. – Can’t be used at interrupt time. – Deadlocks. ® 7 -36
Summary l task. Lock( )/task. Unlock( ): – Prevents other tasks from running. – Use when doing something quick frequently. – Caveat: keep critical region short. l int. Lock( )/int. Unlock( ): – Disables interrupts. – Use to protect resources used by tasks and interrupt service routines. – Caveat: keep critical region short. ® 7 -37
- Slides: 36