Tentamen Datorteknik och realtidssystem, TSEA81 Datum 2018-04-0 Lokal G6 Tid 14-18 Kurskod TSEA81 Provkod TEN1 Kursnamn Datorteknik och realtidssystem Institution ISY Antal uppgifter 5 Antal sidor 16 (inklusive denna sida) Kursansvarig Anders Nilsson Lärare som besöker skrivsalen Anders Nilsson Telefon under skrivtiden 01-28 265 Besöker skrivsalen Cirka 15 och 17 Kursadministratör Gunnel Hässler 01-28 2606 Tillåtna hjälpmedel Betygsgränser Inga Poäng Betyg 41-50 5 1-40 4 21-0 0-20 U Viktig information Alla svar ska ha en motivation om inget annat anges. Om du svarar med programkod räknas kommentarer i programkoden som motivation. Svar som ej är motiverade kan leda till poängavdrag Om inget annat anges ska du anta att schemaläggningsmetoden som används är priority based, preemptive, scheduling Om du är osäker på det exakta namnet för en viss funktion, skriv en kommentar om vad funktionen gör så kommer vi troligtvis att förstå vad du menar. (Detsamma gäller syntaxen för programspråket C.) Skriv läsbart! Oläsbar text kan inte bedömas och ger därmed inga poäng. Lycka till! 1
2
Uppgift 1: Periodiska processer(10p) Ett realtidssystem med två periodiska processer, P1 och P2, ska schemaläggas. Följande krav gäller: P1 ska arbeta/köra under tidsenheter i tidsintervallet [i7, (i + 1)7], där i är ett heltal och i 0. P2 ska arbeta/köra under tidsenheter i tidsintervallet [i5, (i + 1)5], där i är ett heltal och i 0. Varje process aktiveras (är körklar) vid startpunkten för respektive tidsintervall. Det innebär att P1 aktiveras vid var 7:e tidsenhet, med start vid tidpunkten 0 och P2 aktiveras vid var 5:e tidsenhet, med start vid tidpunkten 0. Varje process har en deadline vid slutet av respektive tidsintervall. Det innebär att P1 har en deadline vid var 7:e tidsenhet, med början vid tidpunkten 7 och P2 har en deadline vid var 5:e tidsenhet, med början vid tidpunkten 5. Tänk på att för varje deluppgift nedan tydligt visa och motivera hur du kommer fram till svaret. Ett sätt kan vara att rita en tidslinje där det framgår när och hur länge respektive process kör och var de har sina deadlines. För följande deluppgifter, avgör om processerna uppfyller kraven. Om de uppfyller kraven ange var det finns outnyttjad processortid. Om de inte uppfyller kraven ange vid vilka tidpunkter processerna missar sina deadlines. Eventuellt missade deadlines medför att det arbete som inte utförts inom ett tidsintervall förkastas, dvs det ackumuleras inte till kommande tidsintervall. Ange också den faktiska utnyttjandegraden under ovanstående förutsättningar och krav. (a) (5p) Antag att schemaläggningen Earliest Deadline First (EDF) används. (b) (5p) Antag att schemaläggningen priority based preemptive scheduling (prioritetsbaserad påtvingad schemaläggning) används samt att P1 har högre prioritet än P2.
Uppgift 2: Semaforer(8p) Antag att funktionerna wait och signal för semaforerna i ett realtidsoperativsystem fungerar enligt följande pseudokod: void wait(scb * Mutex) så länge fältet count i Mutex är lika med noll flytta körande process PCB från ReadList till WaitList anropa Schedule minska fältet Count i Mutex med ett void signal(scb *Mutex) nollställ flagga för att anropa Schedule om fältet WaitList i Mutex inte representerar en tom lista flytta ett PCB från listan WaitList till ReadyList sätt flagga för att anropa Schedule öka fältet count i Mutex med ett om flaggan för att anropa Schedule är satt anropa Schedule Betrakta sedan följande program i Simple-OS: #include <simple_os.h> #include <stdio.h> #define STACK_SIZE 5000 /* define task stack spaces */ stack_item A_stack[STACK_SIZE]; stack_item B_stack[STACK_SIZE]; si_semaphore S1; // define semaphore void do_work() int i; volatile int work_data; for(i=0; i < 75000000;i++) work_data=i; 4
/* task A */ void A(void) si_sem_wait(&s1); printf("p\n"); si_wait_n_ms(1000); printf("r\n"); si_sem_signal(&s1); printf("o\n"); do_work(); printf("c\n"); si_sem_wait(&s1); printf("e\n"); si_wait_n_ms(2000); printf("s\n"); si_sem_signal(&s1); printf("s\n"); while(1); /* task B */ void B(void) si_wait_n_ms(500); printf("t\n"); si_sem_wait(&s1); printf("a\n"); do_work(); printf("s\n"); si_sem_signal(&s1); printf("k\n"); while(1); // wait on semaphore // sleep for 1000 ms // signal on semaphore // do some work for some time // wait on semaphore // sleep for 2000 ms // signal on semaphore // wait for ever // sleep for 500 ms // wait on semaphore // do some work for some time // signal om semaphore // wait for ever /* main program */ int main(void) /* initialise simple OS kernel */ si_kernel_init(); /* initialise semaphore to 1 */ si_sem_init(&s1, 1); /* create tasks */ si_task_create(a, &A_stack[STACK_SIZE-1], 10); // high priority si_task_create(b, &B_stack[STACK_SIZE-1], 20); // low priority /* start the kernel, also starting tasks */ si_kernel_start(); return 0; 5
Beskriv steg för steg vad som händer från det att de båda processerna A och B är körklara. Var noga med att tala om vilka processer som är körande, vilka listor dom ligger i vid olika tillfällen, semaforens värde samt motivera varför olika händelser sker. Listornas exakta namn är inte viktigt, bara det framgår vad deras syfte är. Ange också den resulterande utskriften. 6
Uppgift : Await / Cause(10p) Följande programrader är listningar av funktionerna Await och Cause. Tyvärr har vissa av raderna blivit felaktiga. Ange vilka av raderna som är felaktiga och vad det ska stå där för att få korrekt funktion. OBSERVERA! Om du ändrar på redan korrekta rader så medför det poängavdrag! 00 /* Await */ 01 void si_ev_wait(si_event *ev) 02 0 int pid; 04 DISABLE_INTERRUPTS; 05 if (!ready_list_is_empty(ev->wait_list, WAIT_LIST_SIZE)) 06 07 pid = wait_list_insert_highest_prio(ev->wait_list, 08 WAIT_LIST_SIZE); 09 wait_list_insert(pid); 10 11 else 12 1 ev->mutex->counter++; 14 15 pid = process_get_pid_running(); 16 wait_list_remove(pid); 17 wait_list_insert(ev->wait_list, WAIT_LIST_SIZE, pid); 18 schedule(); 19 ENABLE_INTERRUPTS; 20 21 22 /* Cause */ 2 void si_ev_cause(si_event *ev) 24 25 int done; 26 int pid; 27 done = wait_list_is_empty(ev->wait_list, WAIT_LIST_SISZE); 28 DISABLE_INTERRUPTS; 29 ev->mutex->counter--; 0 while(!done) 1 2 pid = ready_list_remove_one(ev->mutex->wait_list, WAIT_LIST_SIZE); wait_list_remove(ev->mutex->wait_list, WAIT_LIST_SIZE, pid); 4 done = wait_list_is_empty(ev->wait_list, WAIT_LIST_SIZE); 5 6 ENABLE_INTERRUPTS; 7 7
Uppgift 4: Processbyte(10p) Betrakta situationen i figur 1 nedan. Två processer finns i ReadyList och en är körande, markerad med pekaren Running. CPU:n har under körning använt den körande processens stack och därmed kanske skrivit över tidigare värden för (Reg) och (PC) varför dessa satts inom parentes då de nu får anses vara inaktuella. Utgå från denna situation och beskriv steg för steg hur ett processbyte går till, med avseende på vad som händer i CPU:n, processernas stackar och TCB. Om du vill kan du i din beskrivning förutom pekarna Running och ReadyList använda pekarna Current (som tillfällig markering under bytet av körande process), Next (för att markera nästkommande körande process) samt WaitList eller eventuellt TimeList. Figur 1: Två processer, en körande. 8
Uppgift 5: Monitor(12p) Man önskar ta fram ett program med tre processer, A, B och C. Processerna ska kommunicera via en gemensam buffer. Processen A ska kontinuerligt producera/skriva data till buffern. Processen B ska konsumera/läsa var tredje data som skrivits i buffern, med början på det första datat, samt skriva ut det data som lästs. Processen C ska konsumera/läsa två på varandra följande data i buffern, med början på det andra datat, samt skriva ut de data som lästs. Låt processen A producera data enligt en uppräkning, såsom: 0, 1, 2,, 4, 5, 6,... osv. Processen B ska då konsumera data enligt följande: 0,, 6,... osv. Processen C ska då konsumera data enligt följande: 1, 2, 4, 5,... osv. Utskriften skulle alltså kunna se ut enligt följande: B:0 C:1 C:2 B: C:4 C:5 B:6... Din uppgift är att skriva programkoden (C-kod) för processerna A, B och C. Själva huvudprogrammet och initieringar är redan gjorda enligt nedan. Du får INTE göra ytterligare globala definitioner eller programsatser. Dvs, du får endast skriva den kod som ingår i processerna A, B och C. #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> #define BUFFER_SIZE 10 /* buffer */ struct int data[buffer_size]; int in_pos; int out_pos; int count; pthread_mutex_t M; pthread_cond_t C; buf; 9
sem_t S1; sem_t S2; sem_t S; void *A_thread(void *unused)... void *B_thread(void *unused)... void *C_thread(void *unused)... int main(int argc, char **argv) pthread_mutex_init(&buf.m, NULL); buf.in_pos = 0; buf.out_pos = 0; buf.count = 0; sem_init(&s1, 0, 0); sem_init(&s2, 0, 0); sem_init(&s, 0, 0); pthread_t A_thread_handle; pthread_t B_thread_handle; pthread_t C_thread_handle; pthread_create(&a_thread_handle, NULL, A_thread, 0); pthread_create(&b_thread_handle, NULL, B_thread, 0); pthread_create(&c_thread_handle, NULL, C_thread, 0); pthread_join(a_thread_handle, NULL); pthread_join(b_thread_handle, NULL); pthread_join(c_thread_handle, NULL); while(1); Observera, din programkod måste vara kommenterad för full poäng. 10
Lösningsförslag fråga 1: 1a Med EDF (Earliest Deadline First) är det möjligt att uppnå 100 procent utnyttjandegrad, vilket också blir fallet här, dvs U e = 1. P2 (alternativt P1) kommer dock att missa sin deadline vid tidpunkten t=5. P1 P2 2 0 1 2 4 5 6 7 8 9 10 11 12 1 14 15 16 17 18 19 20 21 22 2 24 25 26 27 28 29 0 1 2 4 5 1b Med prioritetsbaserad påtvingad schemaläggning och där prioritetsordningen P1 > P2 gäller kommer P1 alltid att klara sina deadlines, men P2 missar deadlines vid tidpunkterna t=5, 10 och 25. Det kommer att finnas outnyttjad processortid under tidsintervallen t=1-14 samt t=4-5. Utnyttjandegraden blir sålunda U e = /5. P1 P2 2 2 1 1 0 1 2 4 5 6 7 8 9 10 11 12 1 14 15 16 17 18 19 20 21 22 2 24 25 26 27 28 29 0 1 2 4 5 Lösningsförslag fråga 2: Programmet skriver ut: P T R O C E S S Här finns tre tillfällen som kan orsaka ett processbyte. När en process gör sleep (och en annan process är körklar), när en process anropar wait (och semaforens värde är 0) samt när en process kör signal (och en högre prioriterad process är körklar). Tre listor blir aktuella, en time-list för då sleep anropas (T), en wait-list för semaforen (W) och en ready-lista för körklara processer (R). A B körande counter T W R 1) A 1 A,B 2) wait A 0 A,B ) sleep(1) sleep(0.5) - 0 A,B - 4) wait (B) 0 A B (B) 5) signal A 1 A,B 6) wait A 0 A,B 7) sleep(2) (B) 0 A B 8) signal A 1 A,B 1) Från det att båda processerna är körklara blir A (högst prioritet) körande. 2) A gör wait (W tom) counter ) printf(p), A gör sleep(1), B blir körande och gör sleep(0.5) varpå båda ligger i T 4) B vaknar först (läggs i R), printf(t), B gör wait (counter==0) och läggs i W 5) A vaknar, printf(r), kör signal (B till R, counter++, schedule anropas), och A med högst prio fortsätter, printf(o), do work, printf(c) 6) A gör wait (counter==1) och fortsätter (counter ), print(e) 11
7) A gör sleep(2) (läggs i T), B blir körande men counter==0 så B läggs i W 8) A vaknar, print(s), gör signal (B till R, counter++, schedule anropas), och A med högst prio fortsätter, printf(s), A går in i oändlig while-loop Lösningsförslag fråga : 00 /* Await */ 01 void si_ev_wait(si_event *ev) 02 0 int pid; 04 DISABLE_INTERRUPTS; 05 if (!wait_list_is_empty(ev->mutex->wait_list, WAIT_LIST_SIZE)) 06 07 pid = wait_list_remove_highest_prio(ev->mutex->wait_list, 08 WAIT_LIST_SIZE); 09 ready_list_insert(pid); 10 11 else 12 1 ev->mutex->counter++; 14 15 pid = process_get_pid_running(); 16 ready_list_remove(pid); 17 wait_list_insert(ev->wait_list, WAIT_LIST_SIZE, pid); 18 schedule(); 19 ENABLE_INTERRUPTS; 20 21 22 /* Cause */ 2 void si_ev_cause(si_event *ev) 24 25 int done; 26 int pid; 27 DISABLE_INTERRUPTS; 28 done = wait_list_is_empty(ev->wait_list, WAIT_LIST_SISZE); 29 0 while(!done) 1 2 pid = wait_list_remove_one(ev->wait_list, WAIT_LIST_SIZE); wait_list_insert(ev->mutex->wait_list, WAIT_LIST_SIZE, pid); 4 done = wait_list_is_empty(ev->wait_list, WAIT_LIST_SIZE); 5 6 ENABLE_INTERRUPTS; 7 12
Lösningsförslag fråga 4: Processbytet sker i följande ordning: 1. Justera fälten i TCB för körande process, om det behövs. T ex för att si wait n ms() har anropats eller att prioriteten har ändrats. 2. Flytta TCB för körande process från ReadyList till en annan lista: TimeList om si wait n ms() har anropats, eller någon WaitList beroende på vad man väntar på.. Leta upp nästa process, den med högst prioritet i ReadyList, och markera den med Next. 4. Markera nu körande process med Current, och markera den process som ska bli körande, dvs Next, med Running. 5. Kopiera PC i CPU till stacken för nu körande process, dvs den markerad med Current. (görs t ex via PUSH) 6. Kopiera Reg i CPU till stacken för nu körande process, dvs den markerad med Current. (görs t ex via PUSH) 7. Sätt SP i TCB för körande process (Current) till samma som SP i CPU. 8. Sätt SP i CPU till SP i TCB för den process som ska starta (Next). 9. Kopiera Reg från stacken för den process som ska starta (Next) till Reg i CPU. (görs t ex via POP) 10. Kopiera PC från stacken för den process som ska starta (Next) till PC i CPU. (görs t ex via POP) I och med sista steget så sker ett hopp till den nya process som ska köra, dvs den som nu är markerad med Running. Pekarna Next och Current har längre ingen betydelse. Lösningsförlag fråga 5: #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> #define BUFFER_SIZE 10 /* buffer */ struct int data[buffer_size]; int in_pos; int out_pos; int count; pthread_mutex_t M; pthread_cond_t C; buf; 1
sem_t S1; sem_t S2; sem_t S; void *A_thread(void *unused) int num = 0; while(1) pthread_mutex_lock(&buf.m); while (buf.count == BUFFER_SIZE) pthread_cond_wait(&buf.c, &buf.m); buf.data[buf.in_pos++] = num++; buf.count++; pthread_cond_broadcast(&buf.c); if (buf.in_pos == BUFFER_SIZE) buf.in_pos = 0; pthread_mutex_unlock(&buf.m); sem_post(&s1); void *B_thread(void *unused) int data; while(1) sem_wait(&s1); pthread_mutex_lock(&buf.m); while(buf.count == 0) pthread_cond_wait(&buf.c, &buf.m); data = buf.data[buf.out_pos++]; buf.count--; pthread_cond_broadcast(&buf.c); if (buf.out_pos == BUFFER_SIZE) 14
buf.out_pos = 0; printf("b:%d\n", data); pthread_mutex_unlock(&buf.m); sem_post(&s2); sem_wait(&s); void *C_thread(void * unused) int data; int i; while(1) sem_wait(&s2); pthread_mutex_lock(&buf.m); for (i=0; i<2; i++) while(buf.count == 0) pthread_cond_wait(&buf.c, &buf.m); data = buf.data[buf.out_pos++]; buf.count--; pthread_cond_broadcast(&buf.c); if (buf.out_pos == BUFFER_SIZE) buf.out_pos = 0; printf("c:%d\n", data); pthread_mutex_unlock(&buf.m); sem_post(&s); int main(int argc, char **argv) pthread_mutex_init(&buf.m, NULL); buf.in_pos = 0; 15
buf.out_pos = 0; buf.count = 0; sem_init(&s1, 0, 0); sem_init(&s2, 0, 0); sem_init(&s, 0, 0); pthread_t A_thread_handle; pthread_t B_thread_handle; pthread_t C_thread_handle; pthread_create(&a_thread_handle, NULL, A_thread, 0); pthread_create(&b_thread_handle, NULL, B_thread, 0); pthread_create(&c_thread_handle, NULL, C_thread, 0); pthread_join(a_thread_handle, NULL); pthread_join(b_thread_handle, NULL); pthread_join(c_thread_handle, NULL); while(1); 16