TDDIU81 Processer och trådar Andreas Dahlberg, Jonathan Doherty, Tony Magnusson, Patrik Ottosson, Rasmus Siljedahl Sammanfattning Den här rapporten innehåller en kort genomgång av allmän process och trådhantering i operativsystem. Sen beskrivs hur detta är implementerat i operativsystemet Pintos. Processer När en process skapas av parentprocess så får den nya childprocessen ett unikt process identifier(pid), för att underlätta identifieringen av alla processer. När den nuvarande childprocessen sedan skapar nya childprocesser blir den själv en parentprocess. När de körts klart så avslutas dom och tas bort med systemanropet exit(). Då avallokers allting som den processen använt av operativsystemet [1]. Det är lite skillnad på vad som händer när en process skapas i Unix jämfört mot Windows. I Unix skapas en ny childprocess med systemanropet fork(). För att parentprocessen lättare ska kunna kommunicera med den nya processen så innehåller den nya processen en kopia av adressutrymmet hos föräldern. Efter fork() så fortsätter båda två att exekveras[1]. I Windows skapas en ny process med funktionen CreateProcess() vilket påminner om fork() på det sätt att en ny process skapas av en parentprocess. Skillnaden mellan är att CreateProcess() måste ladda in ett program till adressutrymmet av childprocessen istället för att ärva adressutrymmet av sin förälder[1]. För att starta en process i Pintos så måste först en ny tråd skapas, det görs genom att kalla på funktionen thread_create(). Thread_create() skapar i sin tur en kernel tråd och en struct thread som sedan läggs till i queuen. Struct thread innehåller en page för thread stacken som är 4kb stor, samt relevant data. Då en process består av denna struct thread betyder det att struct thread är processens process control block (PCB). Det betyder även att struct thread representerar thread control block (TCB) på samma gång. Därav har Pintos en 1:1 mappning mellan user trådar och kernal trådar och PCB och TCB är representerade i samma data struktur. Skapningen av tråden fortsätter i funktionen start_process där en userprocess laddas och körs. Precis som i boken så har processen ett id men i Pintos är det samma som trådens id. En av de viktigaste funktionerna för att hantera processer är ”process_execute” som finns i pintos, denna funktion exekverar det som står på ”command line”. Detta gör den genom att skapa en ny process. När den ska skapar en ny process gör den det genom att skapa en ny ”thread” och schemalägger att funktionen ”start_process” ska köras. Funktionen ”start_process” är i pintos en ”thread” funktion som laddar användarens process och kör den. När en process ska avslutas så ska “process_exit()” köras i pintos och returnera statuskoden. Jämfört med boken Operating system concept då UNIX kör “exit()” utför den samma som pintos att returnera status men också ser till att tömma allt minne. I pintos utförs detta av en egen funktion “process_cleanup”. När “exit()” körs i UNIX, kan “parent” processen använda sig av “wait()” för att vänta på att “child” processen ska avslutas. I pintos finns motsvarande funktion “process_wait()” som väntar på att “child” processen ska dö och returnera exit status[2]. Funktionen ”process_cleanup” är viktig när en process är klar och ska stängas ner eller om en process har kraschat. Den har till uppgift att städa upp den nuvarande processens resurser genom att befria minnet som den tar upp. 1 Varje gång en process ska utföra en “context switch” kallas funktionen “process_activate”, den har som uppgift att ställa in cpu till att köra “user code”, den laddar in “page directory” till cpu’s “page directory” bas register. Som de beskriver i boken Operating system concept är det “interrupts” som orsakar en “context switch”. När detta sker utförs en ”state save” som sparar den nuvarande processens värden för att senare kunna aktivera processen igen. När den aktiveras igen så kör den “state restore” och detta är motsvarigheten till “process_activate” i pintos[3]. Det sista som sker i “process_activate är att den ställer tillbaka “interrupt” så att det återigen kan ske. Schemaläggning I Pintos initieras ready-kön i början av filen thread/thread.c. De funktionerna som används för att schemalägga proccesser är: ”thread_unblock()” lägger den inmatade tråden i ready-kön och sätter trådens läge till THREAD_READY ”thread_block()” låter den inmatade tråden vänta tills thread_unblock() anropas. Trådens läge sätts till THREAD_BLOCKED. Sen schemaläggs en ny tråd för körning. ”running_thread()” hämtar den tråden som körs för tillfället. För att ta reda på vilken tråd som körs så tar man reda på vilken sida stackpekaren är i. Längst upp på sidan finns då struct thread som ger den information man vill ha. ”next_thread_to_run()” hämtar nästa tråd att köras från ready-kön. ”schedule()” använder de två ovanstående funktionerna och byter till den nya tråden. ”schedule_tail()” anropas i slutet av ”schedule()” och sätter den nuvarande trådens läge till THREAD_RUNNING. Sen nollställs thread_ticks och proccessen aktiveras. Om den föregående tråden hade läget THREAD_DYING så frigörs dess minne. ”thread_yield()” lägger den tråden som körs på ready-kön och sätter dess läge till THREAD_READY. Sen anropas ”schedule()” för att starta en ny tråd. ”thread_tick()” anropas via en interupt timer. Funktionen räknar upp en variabel som håller reda på hur många ticks tråden körts. User, idle och kerneltrådarna har varsin räknare. Om tråden har kört sina maximala tics anroppas ”thread_yield()”. Antalet tics varje tråd får köra i ett sträck bestäms av konstanten TIME_SLICE. 2 Bilden nedan visar vilka funktioner som flyttar trådar mellan olika lägen. Schemaläggningsalgoritmen som används är Round-robin. Round-robin schemaläggning fungerar som FIFO-schemaläggning fast med preemption. Varje tråd körs i ordningen som de läggs på ready-kön. Men varje tråd får bara köra under en förutbestämd tid åt gången. När tiden gått ut körs nästa tråd istället. När alla trådar körts börjar man om från början igen. Om en tråd blir färdig innan sitt tidsfönster har gått ut så släpper den CPUn och nästa tråd schemaläggs direkt. Round-robin är en simpel schemaläggningsalgoritm som är enkel att implementera men saknar vissa funktioner såsom prioritering. Det är viktigt att välja rätt storlek på tidsfönstret då det ha en stor inverkan på algoritmens prestanda. Ett för litet värde ger mycket overhead då byten mellan trådar sker väldigt ofta. Med ett för stort värde blir algoritmen i princip en FCFS istället. Trådhantering De states(waiting, running etc) som finns i pintos för trådar är liknande andra operativ som finns. Läser man boken så ser man att nästan alla operativ har likadana states, och det skiljer sig väldigt lite mellan olika operativsystem. Däremot finns det stora skillnader mellan hantering av dessa trådar. Kerneltrådar och usertrådar hanteras inte på samma sätt i olika operativsystem. I Pintos används en one-to-one model. Structen kernal_thread anropas i funktionen thread_create vilket ger att för varje process som startas, så startasen kernal thread som senare blir kopplad till en user thread som senare kör processen. Denna metod är en av de enklaste metoderna att implementera och är använt av till exempel win32 och linux. Nackdel är att det finns ett begränsat antal kernel threads. Det kan innebära att man inte kan starta upp vissa andra user threads, eftersom alla kernels är upptagna. I Pintos nuvarande implementation skapas en ny kerneltråd varje gång man skapar en ny tråd. Genom att modifiera nuvarande struct thread som i nuläget representerar både PCB och TCB och skapa en ny datastruktur för Process Control Block(PCB), kan man skapa nya Thread Control Blocks och lägga till dem till sin process(PCB) genom pekare. Då en ny user thread skapas lägger man till referensen till Process Control Blocket placerar den på kön, sedan när det är denna threads tur att exekvera använder den länken till sin Process Control Block för att få tillgång till processens resurser. Process Control Block bör flyttas till filen där processer hanteras och innehålla följande data: 3 --- PCB --+---------------+ | Tillgängligt | | minne | +---------------+ | Trådar | +---------------+ | Öppna filer | +---------------+ | Schemaläggn.| | Info | | Prioritet | +---------------+ | ID | +---------------+ Referenslista 1. Abraham Silberschatz, Greg Gagne, Peter Baer Galvin. Operating system concept: 3.3.1 Process Creation 90-95. 2. Abraham Silberschatz, Greg Gagne, Peter Baer Galvin. Operating system concept: 3.3.2 Process Termination 95. 3. Abraham Silberschatz, Greg Gagne, Peter Baer Galvin. Operating system concept: 3.2.3 Context Switch 89-90. Thread Control Block kan vara kvar i nuvarande struct thread med följande data: TCB +---------------+ | Stack | | "fil.txt" | +---------------+ | MINNE FÖR | | RESULTAT | +---------------+ | LÄS FIL | +---------------+ | CPU Context/| | Sparade reg. | +---------------+ 4