Download presentation
Presentation is loading. Please wait.
1
CPE555A: Real-Time Embedded Systems
Lecture 8 Ali Zaringhalam Stevens Institute of Technology Spring 2016, arz CS555A – Real-Time Embedded Systems Stevens Institute of Technology 1
2
Outline Threads POSIX API Synchronization
CS555A – Real-Time Embedded Systems Stevens Institute of Technology
3
Midterm Results CS555A – Real-Time Embedded Systems
High: 155 Average: 126 Low: 73 CS555A – Real-Time Embedded Systems Stevens Institute of Technology
4
Threads and Processes Most operating systems therefore support two execution abstractions: The process, Which defines the address space, general process attributes and ownership of resources The thread, Which defines a sequential execution stream within a process sharing resources with other threads A thread is bound to a single process. For each process, however, there may be many threads. Threads are the unit of scheduling Processes are containers in which threads execute CS555A – Real-Time Embedded Systems Stevens Institute of Technology
5
Single and Multithreaded Processes
Shared by all threads. Unique for each thread. CS555A – Real-Time Embedded Systems Stevens Institute of Technology
6
Threads & Processes Single-Threaded Linux Process
Unique for each thread. Shared by all threads. Single-Threaded Linux Process Threads Within a Linux Process CS555A – Real-Time Embedded Systems Stevens Institute of Technology
7
User and Kernel Threads
Which software entity is managing and scheduling threads? Threads can be implemented as a user application or as an OS service User threads - Thread management done at the user-level threads library, without knowledge of the OS kernel Kernel threads - Threads directly controlled by the OS kernel CS555A – Real-Time Embedded Systems Stevens Institute of Technology
8
User vs. Kernel Threads A user-level threads package.
A threads package managed by the kernel CS555A – Real-Time Embedded Systems Stevens Institute of Technology
9
User-Level Threads For speed, implement threads at the user level
A user-level thread is managed by a run-time system The run-time system is linked with your program and manages thread scheduling and other services Each thread is represented simply by: Small control block All thread operations are at the user-level: Creating a new thread switching between threads synchronizing between threads CS555A – Real-Time Embedded Systems Stevens Institute of Technology
10
POSIX Threads POSIX is an IEEE standard that specifies an API similar to UNIX system calls The standard extends the C language with primitives that allow the specification of concurrency POSIX distinguishes between the terms: process and thread A process has its own address space with one or more threads executing in that address space A thread is a single flow of control within a process Every process has at least one thread, the “main()” thread; its termination ends the process All threads share the same address space, but have a separate stack CS555A – Real-Time Embedded Systems Stevens Institute of Technology
11
Pthread Library The Pthread API are implemented in the C pthread library Use “man” to get on-line documentation When compiling under gcc & GNU/Linux, remember the –lpthread option! Example: gcc foo.c –lpthread –o foo CS555A – Real-Time Embedded Systems Stevens Institute of Technology
12
Thread Body A thread is identified by a C function, called body:
The thread starts with the first instruction of its body The thread ends when the body function ends But it's not the only way a thread can die void *my_thread(void *arg) { … } CS555A – Real-Time Embedded Systems Stevens Institute of Technology
13
Pthread API A thread executes a sequential program (e.g. C program). It is created using: int pthread_create(pthread_t * thread_id, pthread_attr_t * attr, void * (*my_thread)(void *), void * arg); Where thread_id is the thread id/handle returned by system my_thread is the sequential program executed by the thread arg is the argument passed to the thread to start pthread_attr_t is a set of pthread attributes. NULL is used to indicate default values. CS555A – Real-Time Embedded Systems Stevens Institute of Technology
14
Thread IDs Each thread has a unique ID
The thread ID of the current thread can be obtained using pthread_t pthread_self(void); Two thread IDs can be compared using int pthread_equal( pthread_t thread1, pthread_t thread2 ); CS555A – Real-Time Embedded Systems Stevens Institute of Technology
15
Thread Attributes Thread attributes specifies the characteristics of a thread Stack size and address Detach state (joinable or detached) Scheduling parameters (priority, …) Pthread API for the attributes object int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); CS555A – Real-Time Embedded Systems Stevens Institute of Technology
16
pthread_join API When your thread join another thread, your thread wants to stay around until the other thread terminates. To do this you call: int pthread_join( pthread_t thread_id, void **value_ptr) thread_id : ID of the thread you are joining value_ptr: thread’s exit status When you join a thread thread_id, your thread blocks until thread_id exits When your thread returns from join, the resources assigned to thread_id will be reclaimed CS555A – Real-Time Embedded Systems Stevens Institute of Technology
17
pthread_exit API A thread can terminate itself by calling
int pthread_exit( void *value_ptr); It returns the value value_ptr to any joining thread. When the thread body ends after the last “}”, pthread_exit() is called implicitly Exception: when main() terminates, exit() is called implicitly When the main thread returns all other threads are aborted. If you want the main thread to exit, but other threads to keep running then call pthread_exit in the main function. CS555A – Real-Time Embedded Systems Stevens Institute of Technology
18
pthread_detach API Suppose you do not want to join a thread you created (why wouldn’t you?) but you want to reclaim the thread’s resources when it exits Then you detach the thread when you do not need to keep track of it anymore When you detach a thread, you tell the system that the thread’s resources can be reclaimed when the thread terminates. It has no other effect on the thread. int pthread_detach( pthread_t thread); Note: Failure to join to or detach threads causes stranded memory resources until the process ends CS555A – Real-Time Embedded Systems Stevens Institute of Technology
19
Thread Resources The main thread is the first thread created in the Linux process and can spawn additional threads When you spawn a thread, Pthread allocates system resources to it (e.g., stack memory) There are three ways to reclaim resources when a thread terminates When the main thread terminates all the threads it has spawned will also terminate and resources used by the threads will be automatically reclaimed A thread can be joined by another thread A thread can be detached CS555A – Real-Time Embedded Systems Stevens Institute of Technology
20
Why void* & void**? Why do we use a pointer (void*) to pass arguments to the thread and return results from the tread? When you make a call on the stack, the value is passed on the stack and return value is returned on the stack When you create a thread, it is not a call on the stack. The threads execute asynchronously each with its own stack So you can’t pass and return on the stack. You must use a pointer to a memory location (heap segment). Because all threads in a process use the same memory space, they can read and write these locations. CS555A – Real-Time Embedded Systems Stevens Institute of Technology
21
Example 1 #include <pthread.h> 2 #include <stdio.h>
3 void* printN(void* arg) { 4 int i; 5 for (i = 0; i < 10; i++) { 6 printf("My ID: %d\n", *(int*)arg); 7 } 8 return NULL; 9 } 10 int main(void) { 11 pthread_t threadID1, threadID2; 12 void* exitStatus; 13 int x1 = 1, x2 = 2; 14 pthread_create(&threadID1, NULL, printN, &x1); 15 pthread_create(&threadID2, NULL, printN, &x2); 16 printf("Started threads.\n"); 17 pthread_join(threadID1, &exitStatus); 18 pthread_join(threadID2, &exitStatus); 19 return 0; 20 } ThreadID is the handle to the thread created by main. Attr=NULL chooses default thread attributes. Passes argument to the thread. Passes pointer to function that the thread must execute. CS555A – Real-Time Embedded Systems Stevens Institute of Technology
22
Example - Continued 1 #include <pthread.h>
A threaded program has a main() function which runs in the “main” or “initial” thread. 1 #include <pthread.h> 2 #include <stdio.h> 3 void* printN(void* arg) { 4 int i; 5 for (i = 0; i < 10; i++) { 6 printf("My ID: %d\n", *(int*)arg); 7 } 8 return NULL; 9 } 10 int main(void) { 11 pthread_t threadID1, threadID2; 12 void* exitStatus; 13 int x1 = 1, x2 = 2; 14 pthread_create(&threadID1, NULL, printN, &x1); 15 pthread_create(&threadID2, NULL, printN, &x2); 16 printf("Started threads.\n"); 17 pthread_join(threadID1, &exitStatus); 18 pthread_join(threadID2, &exitStatus); 19 return 0; 20 } Pthread_join is called to ensure main does not terminate until threadID1 and threadID2 have terminated. Main blocks until these threads terminate Returns exist status of threads. CS555A – Real-Time Embedded Systems Stevens Institute of Technology
23
Asynchronous Execution
The execution of concurrent programs are controlled by a scheduler The programs execute independently of each other: they are asynchronous The scheduler periodically takes control. It suspends the execution of one thread and resumes the execution of another thread The main issue is that the asynchronous execution of threads creates race conditions that must be addressed for the correct execution of concurrent programs CS555A – Real-Time Embedded Systems Stevens Institute of Technology
24
Thread Synchronization
A benefit of using concurrent programming with threads is that the address space is shared so that data can be shared between concurrent programs In order to have predictable behavior, access to shared data must be synchronized across threads Consider a queue shared by multiple threads If one thread removes an element from the queue when another thread is in the midst of accessing it, the concurrent application will fail to operate correctly In order for the concurrent programs to operate correctly, access to shared data must be controlled as a “critical section” Critical sections are areas of code that affect a shared state CS555A – Real-Time Embedded Systems Stevens Institute of Technology
25
Why synchronization? Shared/concurrent access to data Issue
May result in data inconsistency Process A might update some data and then perform several other actions assuming that its modification of the data is still in effect A may be switched out by the scheduler and B changes the shared data Process A Process B Shared data CS555A – Real-Time Embedded Systems Stevens Institute of Technology 25
26
Bounded-Buffer Producer-Consumer Problem
The number of buffers is limited to N Producer and consumer run concurrently Buffers are written/filled by the producer If all N buffers are full, the producer must wait Buffers are read/emptied by the consumer If all buffers are empty, the consumer must wait Process A Producer Process B Consumer Write Read Shared data buffer of fixed size N count 5 … N 1 2 3 4 CS555A – Real-Time Embedded Systems Stevens Institute of Technology 26
27
Data Consistency Issue
Maintaining data consistency requires mechanisms to ensure the orderly execution of the producer and consumer processes for accessing buffers Suppose that we wanted to provide a solution that allows filling of all the buffers. We can do so by both producer and consumer sharing an integer variable count that keeps track of the number of filled buffers Initially, count is set to 0. It is incremented by the producer after it produces a new buffer and is decremented by the consumer after it consumes a buffer. CS555A – Real-Time Embedded Systems Stevens Institute of Technology 27 27
28
Circular Buffer Implementation
Two pointers in: index of the next free position out: index of the first full position count is the number of items currently in the buffer BUFFER_SIZE is the number of buffers CS555A – Real-Time Embedded Systems Stevens Institute of Technology 28
29
Remember Every high-level programming statement is typically translated into several machine-level instructions! Let’s explore the implications of this for shared data Each instruction is guaranteed to execute atomically by the CPU HW. But count++ consists of three instructions and is not guaranteed by CPU HW to be atomic count++; compile register1 = count // LOAD from memory register1 = register // ALU operation count = register1 // STORE in memory CS555A – Real-Time Embedded Systems Stevens Institute of Technology 29
30
Producer while (true) { /* Produce an item and put in nextProduced */
while (counter == BUFFER_SIZE) ; // spin and do nothing buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; counter++; } counter++ could be translated by the complier as register1 = counter (load reg1, counter_address) register1 = register1 + 1 (addi reg1, 1) counter = register1 (store counter_address, reg1) Each instruction executes atomically by the CPU. But the operation as a whole is not atomic CS555A – Real-Time Embedded Systems Stevens Institute of Technology 30 30
31
Consumer while (true) { while (counter == 0) ; // spin and do nothing
The compiler compiles each function separately and can use the same or a different register. The argument here is independent of whether a different or the same register is used because each process has its own stack and may even execute in a different processor. while (true) { while (counter == 0) ; // spin and do nothing nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; counter--; /* Consume the item in nextConsumed */ } counter-- could be implemented as register2 = count (load reg1, counter_address) register2 = register2 – 1 (subi reg1, 1) count = register2 (store counter_address, reg1) Each instruction executes atomically by the CPU. But the operation as a whole is not atomic CS555A – Real-Time Embedded Systems Stevens Institute of Technology 31 31
32
Race Condition - Continued
Consider this execution interleaving with “count = 5” initially: S0: producer execute register1 = counter {register1 = 5} S1: producer execute register1 = register {register1 = 6} S2: consumer execute register2 = counter {register2 = 5} S3: consumer execute register2 = register {register2 = 4} S4: producer execute counter = register {counter = 6 } S5: consumer execute counter = register {counter = 4} Race Condition : a situation where several processes access and manipulate the same data concurrently and the outcome of the execution depends on the particular order in which the access takes place. The scheduler can switch the producer and consumer processes in and out of the CPU in a non-deterministic fashion. Is this the correct answer? If not what is the correct answer? CS555A – Real-Time Embedded Systems Stevens Institute of Technology 32 32
33
Race Condition - Continued
The answer is incorrect because we allowed concurrent access to the shared variable count Solution: Define “critical section” as the region that accesses the shared variable Keep other processes/threads out of the “critical section” while a process/thread is updating a shared variable. CS555A – Real-Time Embedded Systems Stevens Institute of Technology 33
34
Critical Section Problem
Consider system of n processes {p0, p1, … pn-1} Each process has a segment of code, critical section, where Process may be changing common variables, updating a table, writing a file, etc When one process is executing in a critical section, no other process is allowed to execute in the same critical section Critical section problem is to design a protocol that the processes can use to cooperate Each process must ask permission to enter critical section in the entry section Followed by an exit section, then remainder section CS555A – Real-Time Embedded Systems Stevens Institute of Technology 34
35
Critical Section General structure of process pi is do {
remaining section } while (TRUE) Access must be granted at this point. entry section exit section Access permission is given up. CS555A – Real-Time Embedded Systems Stevens Institute of Technology 35
36
Requirements For a Satisfactory Solution
Mutual Exclusion – No two processes may be simultaneously inside their critical section Speed: No assumptions are made about the relative process speed or number of CPUs One process may take a long time while another completes quickly Progress – No process running outside its critical section should block other processes from entering their critical section Bounded Waiting - No process should have to wait arbitrarily long to enter its critical section CS555A – Real-Time Embedded Systems Stevens Institute of Technology 36 36
37
Solutions We Will Examine
Disabling interrupts Three software solutions Two hardware solutions Semaphore Binary semaphore: also known as Mutex Typically implemented as an OS service CS555A – Real-Time Embedded Systems Stevens Institute of Technology 37 37
38
Disabling Interrupts Disable interrupt to disable preemption of the currently-executing task Before entering critical region, disable all interrupts Since scheduling clock operates as just another interrupt, no CPU preemption can occur Disabling interrupt is useful for OS itself (and is used by OS), but not for users…(why?) Question: what if someone disabled the interrupts before you enter the critical region? CS555A – Real-Time Embedded Systems Stevens Institute of Technology 38
39
Using Lock Variable acquaire_lock(A) critical_section(A)
set lock=1 critical_section(A) release_lock(A) set lock=0 acquaire_lock(B) set lock=1 critical_section(B) release_lock(B) set lock=0 Lock variable A software solution A single, shared variable (lock) Assume two processes PA and PB Program in PA tests the variable to see if lock=0 if 0, PA enters the critical region and sets lock=1 if lock=1, it knows that the critical region is occupied by program in PB What is the problem? CS555A – Real-Time Embedded Systems Stevens Institute of Technology 39
40
Taking Turn: Strict Alternation
PB CPU given up. PA CPU given up. Suppose this takes 10 seconds. Suppose this takes only 1 second. turn is a global variable Initially turn=0; so PA can get in first When PA is done, it sets turn=1; so PB can get in, etc. CS555A – Real-Time Embedded Systems Stevens Institute of Technology 40
41
Progress Requirement Suppose noncritical_region(A) is much longer than noncritical_region(B) PA gives up the CPU with turn=1 and starts executing in the nonritical_region(A) PB takes the turn; gives the turn to PA with turn=0; quickly completes noncritical_region(B); goes back to the top of the loop and waits its turn again Because the nonritical_region(A) is long PA doesn’t take its turn for a long time This starves PB because it is ready to enter its critical section but can’t The Progress requirement is violated CS555A – Real-Time Embedded Systems Stevens Institute of Technology 41
42
Peterson’s Solution Restricted to only two processes that alternate execution between their critical sections and remainder sections The LOAD and STORE instructions are atomically executed in the CPU; that is, cannot be interrupted The two processes share two data items int turn boolean flag[2] LOAD/STORE of both are executed atomically by the CPU The variable turn indicates whose turn it is to enter the critical section Both processes can read and write turn The flag array is used to indicate if a process is ready/interested to enter the critical section. flag[i] = true implies that process Pi is ready! flag[i] can be read by both processes but can only be written by process i CS555A – Real-Time Embedded Systems Stevens Institute of Technology 42
43
Peterson’s Algorithm CS555A – Real-Time Embedded Systems
Two processes: Process 0 with ID=0 Process 1 with ID=1 LOAD STORE CS555A – Real-Time Embedded Systems Stevens Institute of Technology 43
44
How it Works enter_region(0)
interested[N] can be read by both But interested[0] is written only by process=0 and interested[1] is written only by process=1 enter_region(0) other=1; interested[0]=TRUE turn=0 while((turn==0) && interested[1]=TRUE)) { spin } return enter_region(0) returns when process=0 can enter critical region enter_region(1) other=0; interested[1]=TRUE turn=1 while((turn==1) && interested[0]=TRUE)) { spin } return enter_region(1) returns when process=1 can enter critical region turn is shared and can be read/written by both The last process to touch turn loses CS555A – Real-Time Embedded Systems Stevens Institute of Technology 44
45
How It Works - Continued
While(1) Enter_region(0) Execute critical section(0) Leave_region(0) Non_critical_region(0) While(1) Enter_region(1) Execute critical section(1) Leave_region(1) Non_critical_region(1) What is the main advantage of this over the strict alternation protocol? Why is this solution still unsatisfactory? Suppose process 1 touches turn last and loses. turn=1. Process=0 gets in first. turn=1; interested[0]=interested[1]=TRUE Process=1 spins because (turn==1) && interested[0]=TRUE) Process 0 is done and calls leave_region(0) Interested[0]=FALSE Process 1 gets in because (turn==1) && interested[0]=TRUE) is now FALSE If process 0 wants to return to the critical region, it calls enter_region(0) again It sets turn=0 As long as process 1 is in the critical section: (turn==0) && interested[1]=TRUE) and process 0 continues to spin When process 1 leaves the critical region, it sets interested[1]=FALSE So process 0 can stop spinning and enters the critical region CS555A – Real-Time Embedded Systems Stevens Institute of Technology 45
46
Analysis What is the main advantage of this over the strict alternation protocol? As long as process i is executing in its non_critical_section, interested[i]=FALSE Process (1-i) can enter the critical region as many times as it wants The progress requirement is satisfied Why is this solution unsatisfactory? Limited to two processes only CS555A – Real-Time Embedded Systems Stevens Institute of Technology
47
Synchronization Hardware
Uniprocessors – could disable interrupts while modifying a shared variable The approach taken by non-preemptive kernels Multiprocessor environment Disabling interrupts requires messaging between the CPUs Inefficient and time-consuming Delays entry and departure in/out of critical section CPUs typically provide hardware support by special atomic instructions (non-interruptable just like other CPU instructions) TestAndSet instruction: allows test memory word and set value Swap: swap contents of two memory words CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 47 47 47 47
48
TestAndSet (TSL) Instruction
TSL is a CPU instruction TSL operates on a memory location just as other memory instructions such as LOAD and STORE lock is a variable defined by applications, stored in memory and shared between processes (or processors) TSL reads & returns the current content of memory at lock On read, the CPU sets the value of lock to TRUE=1 regardless of current content The operation of reading and storing a non-zero value executes atomically without interruption CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 48 48
49
TSL - Continued If the returned value is lock=FALSE=0, it means no one is in the critical section and the process can enter critical section Note that after reading lock, the value is atomically set to TRUE=1 regardless of current value which is returned After a process completes its critical section, it writes lock=FALSE Write is done using the normal STORE instruction If the returned value is lock=TRUE=1, it means another process is in the critical section There is no harm is writing lock=TRUE when the value read is also lock=TRUE TSL can be used in multi-processors: the CPU executing TSL, locks the memory bus and no other processor can access lock CS555A – Real-Time Embedded Systems Stevens Institute of Technology
50
Using TestAndSet Shared Boolean variable lock, initialized to FALSE=0.
Solution below uses two subroutines enter_region is called to enter critical section (busy waiting) leave_region is called to leave critical section Busy waiting: keep looping until lock is available. enter_region(); critical_code(); leave_region(); noncritical_code(); enter_region: TSL REGISTER,LOCK // copy lock to register and set lock to 1 CMP REGISTER,# // was lock zero? JNE enter_region // if it was non zero, lock was set, so loop RET // return to caller; critical region entered The JNE (JUMP if NOT EQUAL) defines the loop. leave_region: STORE LOCK,# // store a 0 in lock RET // return to caller CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 50 50 50 50
51
Swap() Instruction Definition: operates on the contents of two words: two words are swapped atomically in a single instruction without interruption swap A, B A and B are two locations in memory Logically equivalent to the following set of instructions But executed atomically as a single CPU instruction LOAD Reg1, MEM.A // copy content of A into Reg1 LOAD Reg2, MEM.B // copy content of B into Reg2 STORE MEM.A, Reg2 // copy content of Reg2 into A STORE MEM.B, Reg // copy content of Reg1 into B CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 51 51 51 51
52
Solution using Swap() Shared Boolean variable, lock initialized to FALSE Each process has a local Boolean variable key Solution: do { key = TRUE; while ( key == TRUE) { // Spin and swap until key=FLASE swap (&lock, &key ); } // When FALSE is returned to key, it means no other // process is in the critical section // So this process enters critical section // Set lock to FALSE so another process can enter // critical section lock = FALSE; } while (TRUE); Critical Section Remainder Section CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 52 52 52 52
53
Definitions Busy waiting Spin0-lock
Continuously testing a variable until some expected value appears (say TRUE or FALSE) Spin0-lock A lock variable using busy waiting is called a spin-lock All examples we have seen so far use busy-waiting & spin-lock CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 53 53
54
Sleep and Wakeup - 1 Drawback of Busy waiting Wastes CPU cycles
Deadlock scenario possible: suppose two processes with different priorities have the same critical section The lower priority process enters critical region The higher priority process arrives and preempts the lower priority process, while the lower priority is still in the critical section If the higher-priority process wants the lock , there will be deadlock because it will continue to spin in the CPU and prevents the lower-priority process to execute to completion CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 54 54
55
Sleep and Wakeup - 2 Blocking mechanism: block instead of busy waiting
Consider OS providing two services SLEEP system call: causes the calling process to suspend itself until another process wakes it up WAKEUP system call: wakes up a sleeping process specified in the call In the example if the higher-priority process blocks, the lower-priority process can continue to run CS555A – Real-Time Embedded Systems Stevens Institute of Technology
56
Naive SLEEP And WAKEUP Applied to Producer/Consumer Problem
CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 56 56
57
Producer-Consumer Problem
What can be the problem? Concurrent access to count Scenario Buffer is empty: count=0 Consumer reads count=0 and decides to execute SLEEP Before consumer executes SLEEP, it is switched out; the producer is switched in Producer reads count=0; it produces an item and puts it in the buffer. It then increments count=1. Because count=1, the producer sends a WAKEUP signal to consumer The WAKEUP signal is missed by the consumer because it is not sleeping at this time Consumer is now switched back in and continues from the previous state: it calls SLEEP and suspends itself. It will never receive the WAKEUP signal The producer keeps putting items in the buffer until the buffer is full. It then suspends itself. The consumer is also blocked waiting for a WAKUP signal The system is deadlocked Problem: WAKEUP signal sent to a process which is not sleeping is lost Root cause: Condition check, SLEEP and WAKEUP do not execute atomically CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 57 57
58
Root Cause Problem: WAKEUP signal sent to a process which is not sleeping is lost Condition check, SLEEP and WAKEUP do not execute atomically with increment/decrement operations on the “count” variable Change in the variable “count” is not atomic CS555A – Real-Time Embedded Systems Stevens Institute of Technology
59
A semaphore is an object containing a data variable with two operations that are guaranteed to execute atomically Down (P) Up (V) Semaphore Proposed by Dijkstra, introducing a new type of object. The object is referred to as semaphore Variable: semaphore Operations on variable: SLEEP and WAKEUP are referred to as Up and Down Down and Up are implemented as atomic operations A single, indivisible action NOT subject to interruption Down (P): the following operations execute atomically Check semaphore variable to see whether it’s 0, if so, sleep; else, decrement the value and go on Up (V): the following operations execute atomically Increment the semaphore. If processes are waiting on the semaphore (by having executed Down earlier), OS will select one and wakes it up Consider the semaphore count as the number of some resource CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 59 59
60
Producer-Consumer With Semaphores
Use three semaphore variables full: counting the slots that are full; initial value 0 empty: counting the slots that are empty, initial value N mutex: prevent access to the buffer at the same time, initial value 1 (binary semaphore) Synchronization/mutual exclusion mutex is for mutual exclusion. full and empty are for synchronization signaling between the two processes. Both consumer and producer can change empty and full. But the change is executed atomically by the OS. CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 60 60
61
Semaphore Implementation
Semaphore UP and DOWN operations must be executed atomically Typically UP and DOWN are implemented as OS system calls OS disables interrupts Tests the semaphore variable Updates the semaphore variable Puts process to sleep if sem=0 Wakeup a sleeping process if sem>0 Enables interrupts The operations should be executed quickly in a few cycles CS555A – Real-Time Embedded Systems Stevens Institute of Technology CS555A – Real-Time Embedded Systems Stevens Institute of Technology 61 61
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.