260 likes | 393 Views
Outline. Processes Threads Inter-process communication (IPC) Classical IPC problems Scheduling. Issues about IPC. Communication between processes is indispensable. Interrupt is not preferred. Why? How about pipe ? Basic issues How to pass information
E N D
Outline • Processes • Threads • Inter-process communication (IPC) • Classical IPC problems • Scheduling
Issues about IPC • Communication between processes is indispensable. • Interrupt is not preferred. Why? • How about pipe? • Basic issues • How to pass information • How to solve the competition for resources (mutual exclusion) • How to guarantee the dependency (synchronization)
Sharing Resources The next file to print Process A out = 4 in = 7 Process B The next free slot A’s file overwrites B’s!!!
Race Conditions • Two or more processes are reading or writing some shared data, the final result depends on who/when runs precisely • Must be avoided • Solution: mutual exclusion • Critical region/section: the part of program where the shared resource is accessed • No two processes in their critical regions at the same time
Conditions for Sharing Resource • No two processes simultaneously inside their critical regions (mutual exclusion) • No assumption about speeds or number of CPUs • No process running outside its critical region may block other processes • No process waits forever to enter its critical region (no deadlock)
Mutual Exclusion Using Critical Regions A enters critical region A leaves critical region Process A Process B T3 T4 T2 T1 Time
Achieve Mutual Exclusion • Busy waiting • Sleep and wakeup • Semaphores • Mutexes • Monitors • Message passing • Barriers
Busy Waiting – Disabling Interrupts • Enter critical region disable interrupts … enable interrupts leave critical region • Simplest, intuitive • Why disable interrupt? No process switching in critical region • Problem: lose control of user processes • Not work in the case of multiple CPUs • Good for kernel.
Busy Waiting – Lock Variables • Disable interrupt is not good. • Any software solution? • Lock variable: single, shared variable • Before entering critical region, test and set • Race condition on the lock variable • After one process reads the lock but before it sets to 1, switching happens. • Problem: test and set lock variable must be inseparable, i.e., atomic • Ask for help from hardware
TSL: Help from Hardware • Problem of lock variables • Test, if 0, enter, else wait • What if both process test 0 and enter? • TSL instruction: TSL RX, LOCK • Read, update and write-back • All three operations are indivisible Leave_region: MOV LOCK, #0 RET Enter_region: TSL REG, LOCK CMP REG, #0 JNE Enter_region RET
Busy Waiting: Strict Alternation // Initially turn=0 While (TRUE){ while (turn!=1); /*loop*/ critical_region(); turn=0; non_critical_region(); } While (TRUE){ while (turn!=0); /*loop*/ critical_region(); turn=1; non_critical_region(); } Process 1 Process 0 A process may be blocked by another one not in its critical region! It requires strictly alternate in using critical regions. Process 0 and 1 use critical region alternatively in a strict order!
Get Rid of Strict Alternation • Peterson’s solution: combine taking turns with the idea of lock variables • Two procedures • enter_region and leave_region • For each process • Call enter_region before entering its critical region • Call leave_region after leaving its critical region Process 1:……Enter_region(1);critical_part;Leave_region(1);….. Process 0:……Enter_region(0);critical_part;Leave_region(0);…..
C Code of Peterson’s Solution int turn;// shared variable. whose turn is it? int interested[2];//shared variable. all values initially FALSE Void enter_region(int process) //parameter: 0 or 1 { int other; //local variable. number of the other process other=1-process; //the opposite of process interested[process]=TRUE; //show that you are interested turn=process; //set flag while (turn==process && interested[other]==TRUE); } 1 2 3 Void leave_region(int process) //process: who is leaving? { interested[process]=FALSE //departure from critical region }
void enter_region(int 1) //process=1 { int other; //local variable other=1-process=0; interested[1]=TRUE; turn=1; while (turn==1 && interested[0]==TRUE); } void enter_region(int 0) //process=0 { int other; //local variable other=1-process=1; interested[0]=TRUE; turn=0; while (turn==0 && interested[1]==TRUE); } 1 1 2 2 3 3
Problems of Busy Waiting • Waste CPU time • Some processes may never get chance to run • Priority inversion problem • E.g., L in critical region, while H is busy waiting • Solution: block instead of waiting • sleep and wakeup operations • sleep(): cause the caller to block, until another process wakes it up • wakeup(pid): wake up the process specified by pid
Producer-Consumer Problem • Elements: producer, consumer, buffer • Producer: put items into buffer • Consumer: take items out of buffer • Buffer: fixed size holder of items producer items buffer items consumer
#define N 100 // number of slots in the buffer int count = 0; // number of items in the buffer void producer(void){ int item; while (TRUE){ // repeat forever item = produce_item(); // generate next item if (count == N) sleep();// if buffer is full, go to sleep insert_item(item); // put item into buffer count = count+1; // increment count of items in buffer if (count == 1) wakeup(consumer);// was buffer empty? } } void consumer(void){ int item; while (TRUE){ // repeat forever if (count == 0) sleep();// if buffer is empty, go to sleep item = remove_item(); // take item out of buffer count = count-1; // decrement count of items in buffer if (count == N – 1) wakeup(producer);// was buffer full? consume_item(item); // print item } } Switch to producer. Lost-wakeup call!
Problem: Lost-Wakeup • Race condition on the shared variable: count • Buffer is empty and consumer reads the counter as value 0. But before consumer can make the sleep() call, CPU switches to producer. Producer inserts an item, sets counter to 1 and sends a wakeup call to consumer. However, consumer is not sleeping yet, the wakeup signal is lost! • Now CPU switches back to consumer and it finally makes the sleep() call- consumer is asleep now. • Producer will fill up the buffer sooner or later, and it goes to sleep as well. • Both are sleeping!
Semaphores • Two atomic operations: down and up • Suppose s is a semaphore • down(&s):s--; if s < 0 then sleep • up(&s):s++; if s <= 0 then wake up one of the processes blocked by down operation on s. • up operation never blocked! • What’s the value of s now? Don’t know • Can only be accessed by the two operations after initialization.
Atomic Operations • Both down and up are as a single, indivisible atomic actions • Value < 0 indicates some process sleeping • Solve the lost-wakeup problem (why?) • How to implement these atomic operations? • Disable all interrupts • Use TSL instruction
Use of Semaphores • Mutual exclusion • Semaphore s is initialized to 1. • Call down(&s) before enter critical region • Call up(&s) after leave critical region • Can be used for buffer in Producer-Consumer problem • Synchronization • Guarantee certain event sequences do or do not occur • E.g., the Producer stops running when the buffer is full.
#define N 100 // number of slots in the buffer semaphore mutual = 1; // controls access to critical region semaphore empty = N; // counts empty buffer slots semaphore full = 0; // counts full buffer slots void producer(void){ int item; while (TRUE){ item = produce_item(); // generate something to put in buffer down(&empty); // decrement empty count down(&mutual); // enter critical region insert_item(item); // put new item in buffer up(&mutual);// leave critical region up(&full);// increment count of full slots } } void consumer(void){ int item; while (TRUE){ down(&full);// decrement full count down(&mutual); // enter critical region item = remove_item(); // take item from buffer up(&mutual);// leave critical region up(&empty);// increment count of empty slots } } Producer-Consumer using Semaphores
Mutexes: Binary Semaphores • Achieve mutual exclusion • Two states: unlocked (0) / locked (1) • Two operations: mutex_lock(&m) and mutex_unlock(&m) • For a process • Call mutex_lock before entering critical region • Call mutex_unlock after leaving critical region • Can be implemented in user space with TSL instruction.
Implementation of Mutex mutex_lock: TSL REGISTER, MUTEX | copy mutex to register and set mutex to 1 CMP REGISTER, #0 | was mutex zero? JZE ok | if it was zero, mutex was unlocked, so return CALL thread_yield| mutex is busy; schedule another thread JMP mutex_lock | try again latter ok: RET | return to caller; critical region entered mutex_unlock: MOV MUTEX, #0 | store a zero in mutex RET | return to caller Notice: thread_yield is used. Busy waiting does not work here. (Why?)
Problems with Mutual Exclusion • Diskette and CD-Rom protected by mutexes • Process A: got diskette, waiting CD-Rom • Process B: got CD-Rom, waiting diskette • A deadlock! Blocked forever • Programming with semaphores or mutexes is tricky