Operativsystem - Processkommunikation Mats Björkman 2015-01-26
Innehåll Processer (föreläsning 2) Processmodell Processtillstånd Trådar Processkommunikation (föreläsning 3, den här) Semaforer Monitorer Meddelandesystem Skedulering/schemaläggning (föreläsning 4) Kriterier för en skedulerare Skeduleringsalgoritmer
Lärandemål Varför behövs processkommunikation och processynkronisering? Race condition, ömsesidigt uteslutande, kritiska avsnitt Mekanismer för kommunikation/synkronisering Semaforer, meddelandeöverföring, delat minne
Mer begrepp Prioritet: Olika processer kan ha tilldelats olika prioritet, vilket betyder att skeduleraren tar hänsyn till detta i valet av nästa process att köra (mer i nästa föreläsning)
Förtydligande: Multitrådade kärnor Moderna OS-kärnor är oftast multitrådade, vilket betyder att flera aktiviteter kan vara på gång samtidigt i OS-kärnan Detta är en anledning till att varje process behöver en egen kernel stack, exv. kan flera processer ha systemanrop som pågår samtidigt
Multitasking=> samtidighet Wikipedia: Synchronization "In computer science, especially parallel computing, synchronization means the coordination of simultaneous threads or processes to complete a task in order to get correct runtime order and avoid unexpected race conditions."
Multitasking=> samtidighet Processer som är omedvetna om varandra: Oberoende tillämpningar Konkurrerar om samma resurser Processer medvetna om varandra: Samarbetar genom att dela resurser En process kan vara beroende av info från en annan
Processkommunikation och synkronisering Varför kommunikation och synkronisering? Odelbara resurser (ömsesidigt uteslutande) Synkronisera aktiviteter Undvika baklås (deadlock/livelock) i systemet Kommunikation mellan processer Normally processes work independently of each other, however sometimes co-operation is necessary. Therefore IPC is necessary.
Processkommunikation och synkronisering forts. Kommunikationsmetoder Semaforer Meddelandeöverföring Delat minne
Ömsesidigt uteslutande Det finns resurser som endast en process i taget bör få använda (exv. enkel skrivare utan egen hantering av samtidiga utskriftsjobb) Ömsesidigt uteslutande (mutual exclusion) kallas egenskapen vi vill garantera för dessa resurser
Ömsesidigt uteslutande Garantin innebär att det aldrig får finnas en risk för att mer än en process använder resursen samtidigt
Exempel ur verkligheten Ur en gammal OS-kurs i Uppsala: En enkelspårig järnvägssträcka i Anderna mellan Peru och Bolivien (sic) måste ha ett signalsystem för att garantera att max ett tåg i taget är ute på linjen
Exempel ur verkligheten Problemet uppstår förstås när det kommer tåg från båda hållen precis samtidigt Vi måste hitta en lösning som garanterar ömsesidigt uteslutande utan att resultera i baklås
Exempel ur verkligheten Lösningen skall passa att koda in i en algoritm Ofta ställs dessutom kraven att algoritmen skall vara rättvis och fri från svält
Kritiskt avsnitt En typisk mekanism för att åstadkomma ömsesidigt uteslutande i kod är kritiska avsnitt (critical region el. critical section) Kritiska avsnitt är kodavsnitt som bara en process i taget får exekvera i Exempel: Koden som exekveras för att skriva ut på skrivaren
Ömsesidigt uteslutande Exempel utan kritiskt avsnitt: Process A Process B Börjar skriva ut ett dokument på skrivaren. Blir avbruten av process B som har högre prioritet. Avbryter A och skriver ut sitt dokument. Blir klar och avslutar. Fortsätter utskriften av det första dokumentet. Resultatet blir ihopblandade utskrifter. Many resources cannot be shared by multiple processes, e.g., printers, files, streamers. Every user of the resource has a portion of the code in which it modifies it. This portion is called Critical Section.
Ömsesidigt uteslutande Lösningen blir att hela den kod som skriver ut på skrivaren måste vara ett kritiskt avsnitt, så att inte process B kan skriva ut när A redan börjat skriva ut, utan B måste vänta tills A är klar
Ömsesidigt uteslutande Ett annat exempel handlar om åtkomst till delade variabler
Ömsesidigt uteslutande forts. Race condition Example: int saldo= 1000; Thread A Thread B void A(void){ void B(void){ int peng; int peng; peng=saldo; peng=saldo; peng=peng-100; peng=peng+1000; saldo=peng; saldo=peng; … … } } Scenario 1 900 1900 When the result of a computation depends on in which order two or more processes executes while sharing data it is called a Race Condition. RESULTAT: saldo=1900
Ömsesidigt uteslutande forts. Race condition Example: int saldo= 1000; Thread A Thread B void A(void){ void B(void){ int peng; int peng; peng=saldo; peng=saldo; peng=peng-100; peng=peng+1000; saldo=peng; saldo=peng; … … } } Scenario 2 2000 900 Discuss the concept of reentrant code. Koppla till labbarna med att det är en viktig sak att hålla i minnet. RESULTAT: saldo=900
Ömsesidigt uteslutande Även i detta exempel gäller det att skapa ett kritiskt avsnitt, i detta fall är det koden från innan processen läser saldot tills efter den har skrivit det nya saldot som behöver utgöra ett kritiskt avsnitt
Ömsesidigt uteslutande genom kritiska avsnitt
Ömsesidigt uteslutande Nu behöver vi några mekanismer som hjälper oss att skapa kritiska avsnitt
Ömsesidigt uteslutande forts. Fyra krav: Aldrig fler än en process samtidigt i kritiska avsnittet Inga antaganden om processhastighet eller antal CPU:er Ingen process som inte är i kritiska avsnittet får blockera en annan process Ingen process skall behöva vänta i evighet på exekvering Undvik baklås, svält och busy waiting (“rastlös väntan”) Hur kan detta lösas? Algoritmiskt i tillämpningen Algoritmiskt i tillämpningen + hårdvarustöd Interrupt disable Test And Set-primitiv OS-stöd: semaforer, meddelandeöverföring Vad är det för krav om man vil implementera ME One process at the time: Mutual exclusion Liveness: No livelock or starvation Algorithm in application: A variable that marks if a CS is occupied or available. THIS is difficult to achieve!!! Interrupt disable: Disables the interrupts so that timer interrupt cannot occur, you will execute until enabled again. TAS: Exists in 68k processors
Ömsesidigt uteslutande - Algoritm i tillämpningen int turn = 0; void thread0(){ void thread1(){ while(1){ while(1){ // some code // some code while(turn!=0) ; while(turn!=1) ; // Critical Section // Critical section turn = 1; turn = 0; } } } } Fungerar denna algoritm? Finns det nackdelar med denna lösning? Yes it does work, but not very good. However it implies a preordered sequences of access, 0 1 0 1 0 1… If thread 0 wants to access it twice and thread 1 does not want it at all Furthermore it implies busy waiting… And an awareness of all users of the section. Can result in deadlock, if one thread does not want to use the CS.
Ömsesidigt uteslutande - Algoritm i tillämpningen int turn = 0; void thread0(){ void thread1(){ while(1){ while(1){ // some code // some code while(turn!=0) ; while(turn!=1) ; // Critical Section // Critical section turn = 1; turn = 0; } } } } Fungerar denna algoritm? Finns det nackdelar med denna lösning? Yes it does work, but not very good. However it implies a preordered sequences of access, 0 1 0 1 0 1… If thread 0 wants to access it twice and thread 1 does not want it at all Furthermore it implies busy waiting… And an awareness of all users of the section. Can result in deadlock, if one thread does not want to use the CS.
Ömsesidigt uteslutande - Algoritm i tillämpningen int turn = 0; void thread0(){ void thread1(){ while(1){ while(1){ // some code // some code while(turn!=0){} //spin while(turn!=1){} //spin // Critical Section // Critical section turn = 1; turn = 0; } } } } Fungerar denna algoritm? Finns det nackdelar med denna lösning? Yes it does work, but not very good. However it implies a preordered sequences of access, 0 1 0 1 0 1… If thread 0 wants to access it twice and thread 1 does not want it at all Furthermore it implies busy waiting… And an awareness of all users of the section. Can result in deadlock, if one thread does not want to use the CS.
Ömsesidigt uteslutande - Algoritm i tillämpningen (bättre?) boolean interested[2] = {false,false}; void thread0(){ void thread1(){ while(1){ while(1){ // some code // some code interested[0] = true; interested[1] = true; while(interested[1]){} while(interested[0]){} // Critical Section // Critical section interested[0] = false; interested[1] = false; } } } } Fungerar denna algoritm? Finns det nackdelar med denna lösning? No it does not work. Potential deadlock, also uses busy waiting. Consider thread0 to execute until flag[0] = true, and thread1 interupts, setting both flags to true, DEADLOCK!!
Ömsesidigt uteslutande - Algoritm i tillämpningen (nu då?) int int loser; boolean interested[2] = {false,false}; void thread0(){ void thread1(){ while(1){ while(1){ // some code // some code interested[0] = true; interested[1] = true; loser = 0; loser = 1; while(loser==0 && while(loser==1 && interested[1]) {} interested[0]) {} // Critical Section // Critical section interested[0] = false; interested[1] = false; } } } } Fungerar denna algoritm? Finns det nackdelar med denna lösning? Peterson’s algorithm 1981 (boken 2.3.3) Ja, den fungerar. Det är fortfarande busy wait i while-loopen
Ömsesidigt uteslutande - Algoritm i tillämpningen med hårdvarustöd void thread0(){ void thread1(){ while(1){ while(1){ // some code // some code interrupt_disable(); interrupt_disable(); // Critical Section // Critical section interrupt_enable(); interrupt_enable(); } } } } Fungerar denna algoritm? Finns det nackdelar med denna lösning? Yes it does work. However it is a security risk! It is possible to outmaneuver the operating system. Consider that a thread enters the critical section and then crashes. The complete system is choked. Evil programmers can deliberately hang computers.
Ömsesidigt uteslutande - Algoritm i tillämpningen med hårdvarustöd Test and Set (TAS) Sätter en variabel och returnerar gamla värdet Läsningen av gamla värdet och sättningen av nya är garanterat atomärt (odelbart) int TAS(char *flag){ int result; __asm{ tas flag bne alreadySet move #0,&result bra exit alreadySet: move #1,&result exit: } return result; 68k code. Yes it does work. Yes, it still uses busy waiting. However requires support from hardware. 68k processors have this
Ömsesidigt uteslutande - Algoritm i tillämpningen med hårdvarustöd char flag=0; void thread0(){ void thread1(){ int TAS(char *flag){ while(1){ while(1){ int result; // some code // some code __asm{ while(TAS(&flag)==1){} while(TAS(&flag)==1){} tas flag // Critical Sec. // Critical sec. bne alreadySet flag=0; flag=0; move #0,&result } } bra exit } } alreadySet: move #1,&result exit: } return result; Finns det nackdelar med denna lösning? 68k code. Yes it does work. Yes, it still uses busy waiting. However requires support from hardware. 68k processors have this
Test and set Varianter på test and set kan heta: Compare and swap Atomic exchange Samma princip: en odelbar operation där en variabel ändras och det gamla värdet kontrolleras
Busy wait Alla lösningar (hittills) har använt busy wait, den process som måste vänta ligger i en tight loop och kollar och kollar och kollar… Slöseri med CPU-tid, vem kan hjälpa?
Ta-daa! Is it a spin lock? Is it a test-and-set instruction? No! It’s… …The Operating System!
OS-stöd för kommunikation och synkronisering
Semaforer Ett verktyg för att åstadkomma ömsesidigt uteslutande Finns i så gott som alla operativsystem
Semaforer Två versioner: Binära semaforer kan vara 0 eller 1 Räknande semaforer kan vara 0
Semaforer Binära semaforer används när det finns en instans av en resurs och den kan användas av en process i taget Exempel: Lås på toadörr. En användare kan gå in på toa och vrida om låset. Övriga får då vänta till dess den som använder toan låser upp dörren
Semaforer Räknande semaforer används när det finns flera instanser av resursen och/eller flera processorer kan samsas Exempel: En ut/en in på krogen. Det finns ett maximalt antal tillåtna samtidiga besökare. När maxantalet är uppfyllt måste nästa besökare vänta på att någon lämnar lokalen.
Semaforer forts. En semafor har två operationer: wait(semaphore); räknar ner signal(semaphore); räknar upp Alternativa namn: take/give, acquire/release, down/up, P/V (holländska Proberen/Verhogen ≈ försök/höj) Wait = down = semTake Signal = up = semGive
Mutex (MUTual EXclusion) En binär semafor kallas ibland mutex En mutex används som ett lås (kom ihåg toadörren), operationerna kallas ibland mutex_lock (wait) respektive mutex_unlock (signal) Wait = down = semTake Signal = up = semGive
Semaforer forts. Om en semafor är 1 (eller mer) och en process anropar wait(…), så kommer semaforen att räknas ned och processen får fortsätta Wait = down = semTake Signal = up = semGive
Semaforer forts. Om en semafor är 0 och en process anropar wait(…), så kommer processen att flyttas till ett väntetillstånd (wait/sleep) och sättas i en kö associerad till semaforen Processen blir kvar i kön till dess en annan process anropar signal(…) Wait = down = semTake Signal = up = semGive
Semaforer forts. När en process anropar signal(…) tittar operativsystemet om det finns någon process i väntelistan till den semaforen. Om det finns någon i kön, så väcks den första processen i kön upp och får fortsätta exekvera Om väntelistan är tom, räknas semaforen upp med 1 Wait = down = semTake Signal = up = semGive
Semaforer forts. OS:et löser problemet med busy wait: När en process måste vänta behöver den inte ligga och spinna runt runt och testa om den får fortsätta Operativsystemet försätter istället processen i ett väntetillstånd, och lovar att väcka upp processen när den kan fortsätta Systemet slipper slösa CPU-tid på busy wait Wait = down = semTake Signal = up = semGive
Semaforer forts. Operativsystemet tillhandahåller även rättvisa: Om flera processer med samma prioritet blir väntande på wait(…), så kommer operativsystemet att först väcka den som anropade wait(…) först (Vid busy wait och exv. test_and_set är det slumpmässigt vilken av de väntande som har tur att testa precis vid rätt tid) Wait = down = semTake Signal = up = semGive
Semaforer forts. Semaforoperationerna måste vara odelbara (atomära) Operativsystemet garanterar detta Väntande processer sätts tillståndet till väntande (wait/sleep) läggs in i väntelistan på en semafor Introducera en atomosk (icke delbar, avbrytbar) instruktion/operation Exempel på prio inversion .
Semaforer forts. Problem med semaforer Priority inversion: en process med hög prioritet kan bli väntande på en semafor som en process med lägre prioritet har låst Baklås: Process 0 har låst semaforen A och väntar på semaforen B, Process 1 har låst semaforen B och väntar på semaforen A Introducera en atomosk (icke delbar, avbrytbar) instruktion/operation Exempel på prio inversion .
Semaforer forts. Skrivarproblemet löst med semaforer: Process A Process B Ett skrivarjobb begärs. Processen anropar wait(lpr). Eftersom skrivaren är ledig räknas semaforen ner till 0. Utskriften startar. A blir avbruten av process B som har högre prioritet. B vill också skriva ut. B anropar wait(lpr). Eftersom semaforen redan är 0 blir B försatt i väntetillstånd (wait/sleep). A slutför utskriften av det första dokumentet. Anropar signal(lpr). B plockas ut semaforkön och kan skriva ut. Anropar signal(lpr).
Semaforer forts. WAIT(S) SIGNAL(S) LOCK LOCK N N S > 0 Y Y UNLOCK EMPTY N S > 0 FIND CURRENT SEMAPHORE DEQUEUE PROCESS QUEUE PROCESS DESCRIPTOR DESCRIPTOR Y Y S = S - 1 S = S + 1 MOVE FROM READY QUEUE TO WAITING ADD TO READY QUEUE ADD TO SEMAPHORE QUEUE UNLOCK UNLOCK EXIT TO EXIT TO DISPATCHER DISPATCHER
Processynkronisering Uppgift: synkronisera aktiviteter som måste ske i viss ordning Exempel: Producent/konsumentproblemet En process producerar meddelanden Meddelandena placeras i en kö En annan process läser meddelandena Problem: Begränsad köstorlek
Producent/konsument När producenten har producerat ett meddelande skall konsumenten kunna konsumera det När kön är full måste producenten vänta tills konsumenten konsumerat ett meddelande När kön är tom näste konsumenten vänta tills producenten producerat ett meddelande
Producent/konsument Exempel: Vi använder tre semaforer: En räknande semafor för antalet tomma platser En räknande semafor för antalet fulla platser En binär semafor (mutex) för att få ömsesidigt uteslutande vid åtkomst till den delade kön
Producent/konsumentproblemet – med semaforer #define N 100 semaphore mutex = 1; semaphore empty = N; semaphore full = 0; void producer(void) { int item; while(TRUE){ produce_item(&item); wait(&empty); wait(&mutex); enter_item(item); signal(&mutex); signal(&full); } /* Number of slots in the buffer */ /* Mutual exclusion */ /* Number of empty slots in the buffer */ /* Number of full slots in the buffer */ void consumer(void) wait(&full); remove_item(&item); signal(&empty); consume_item(item); Mutex implements mutual exclusion in adding removing Full empty signals when buffer is full and empty respectively
Producent/konsumentproblemet – med semaforer – felaktigt #define N 100 semaphore mutex = 1; semaphore empty = N; semaphore full = 0; void producer(void) { int item; while(TRUE){ produce_item(&item); wait(&empty); wait(&mutex); enter_item(item); signal(&mutex); signal(&full); } /* Number of slots in the buffer */ /* Mutual exclusion */ /* Number of empty slots in the buffer */ /* Number of full slots in the buffer */ void consumer(void) wait(&full); remove_item(&item); signal(&empty); consume_item(item); What happens if we exchange the order of two wait commands???? We will have deadlock in the system consider the following: There are a few items in the buffer when the consumer starts to execute. It empties the buffer and restarts, executing the wait(&mutex). This is ok, but then it tries to obtain the “full” semaphore and fails since it is zero. It sleeps and the produces executes. Wait(&empty) is fine, but it cannot get the mutex semaphore. DEADLOCK. IT IS UP TO THE PROGRAMMER TO LOCK THE SEMAPHORES IN A CORRECT ORDER!!!!!
Semaforer Semaforer i kernel space vs user space Semaforer i kernel space => systemanrop till OS:et Användbara för synkronisering mellan processer Kräver inte delat minne mellan processerna Windows: CreateSemaphore(), CreateMutex() Unix: semget() Semaphore Standard POSIX: sem_init() Semaforer i user space => hanteras i tillämpningen Användbara för synkronisering mellan trådar i samma process Använder delat minne (plus ofta någon form av stöd från OS:et) Windows: CriticalSection() – anropar endast kärnan när någon behöver ställa sig i kö Linux Futex (Fast userspace mutex): kräver TAS el. liknande + systemanrop för köhanterning
Meddelandesystem Processkommunikation genom meddelanden Två primitiver send receive Direkt kommunikation, eller Indirekt kommunikation
Meddelandesystem - Direkt kommunikation Process to Process IPC Send(toProcess, message); Receive(fromProcess, message); Skicka “broadcast” till flera mottagande processer Synkron överföring (Rendezvous) Ingen buffring, processerna blockeras tills överföringen sker Analogi: Telefon Asynkron överföring Buffrad kommunikation, ingen blockering Analogi: telefonsvarare
Meddelandesystem - Indirekt kommunikation Kommunikation via brevlåda (mailbox) En mailbox innehåller En buffert Ett namn Två möjligheter Mailboxen ägs av en process Mailboxen ägs av OS:et Mailbox owned by the process P1 P2 Mailbox owned by the OS P1 P2 P3
Producent/konsumentproblemet – med meddelandeöverföring #define N 100 void producer(void){ int item; message m,token; while(1){ produce_item(&item); receive(consumer,&token); build_mesg(&m,&item); send(consumer, &m); } void consumer(void){ int item, i; for(i=0;i<N;i++) send(procucer,&token); receive(producer,&m); extract_item(&m); send(producer, &token); consume_item(item); Setup, buffered asynchronous message passing, if no message, blocking on receive. The consumer creates N tokens, this is to avoid a overfull queue, compare this to the full, empty semaphores in the semaphore example. FACT: Semaphores, monitors and message passing systems can be built from each other!!!
Delat minne De flesta operativsystem stöder det Speciella primitiver för skapande och åtkomst Felkällor: synkronisering måste bli rätt Inte bra för distribuerade system Dock snabbt och effektivt om processerna kan dela fysiskt minne
Delat minne Det delade minnet mappas in i båda processernas adressrymd Shared Memory Discuss the implications of this, the problems with keeping track of shared and local memory… Windows: CreateFileMapping(), MapViewOfFile() Linux/Unix: shm_open(), mmap()
Lärandemålen igen Varför behövs processkommunikation och processynkronisering? Race condition, ömsesidigt uteslutande, kritiska avsnitt Mekanismer för kommunikaiton/synkronisering Semaforer, meddelandeöverföring, delat minne