Operativsystem: Lösningar till tentamen 2023-05-29

Observera att detta är förslag på lösningar. Det kan finnas andra lösningar som också är korrekta. Det kan hända att en del av lösningarna är mer omfattande än vad som krävs för full poäng på uppgiften, eller att de bara hänvisar till var man kan läsa svaren. Dessutom har det inträffat i världshistorien att lärare skrivit fel i lösningsförslagen. Jag har förstås aldrig gjort det, men andra. Är det verkligen någon som läser såna här inledande texter? Kanske skummar de början och hoppar över resten. Jag vet inte. Det kan vara så. Rabarber rabarber rabarber. Det har jag hört att statisterna får säga på filminspelningar när det ska vara bakgrundssorl från en restaurang eller liknande. Här nedan kommer lösningsförslagen till uppgifterna.

Uppgift 1 (3 p)

En dator har många olika uppgifter som den behöver utföra för att underlätta för användarna. En del uppgifter utförs av operativsystemet, andra av applikationsprogram.

a) Ange en uppgift som det är operativsystemet som utför.

Svar:

Att hantera processer, som att skapa, schedulera och ta bort dem. Att hantera användaridentiteter, som att skapa dem, ta bort dem och ge dem rättigheter, och även att kontrollera de rättigheterna så de inte överskrids. Att hantera sekundärminnet med filer och filkataloger, och möjliggöra för programmen att läsa och skriva filerna.

b) Ange en uppgift som operativsystemet inte utför.

Svar:

Redigering av text, ljud, bilder och andra data i olika program, till exempel Photoshop. Spel.

c) Ange en uppgift där det inte är uppenbart ifall operativsystemet eller ett applikationsprogram ska lösa den.

Svar:

Här kan man ge olika svar. En känd historisk händelse är "webbläsarkriget" på 1990-talet. Webben var ganska ny, och webbläsaren Netscape Navigator var helt dominerande. Den var ett applikationsprogram som, precis som andra applikationsprogram, man fick ladda ner (eller snarare läsa in från CD-skiva, för det var ju på 1990-talet) och installera på sin dator. Microsoft hävdade då att deras konkurrerande webbläsare, Internet Explorer, var en del av operativsystemet Windows. De skickade med den med varje kopia av Windows, och hävdade att den inte gick att avinstallera eftersom den var en integrerad del av operativsystemet. Eftersom alla Windows-användare var tvungna att ha Internet Explorer, och nästan alla vanliga datorer använde Windows, lyckades Microsoft konkurrera ut Netscape, och Internet Explorer blev den dominerande webbläsaren.

Andra exempel: Virusskydd (som kan vara inbyggt i operativsystemet eller ett separat program). Trådhantering (som kan ha stöd av operativsystemet och kärnan, elle vara ett separat paket med enbart user space-trådar).

Uppgift 2 (5 p)

En mikrokärna är en operativsystemkärna där man flyttat ut delar av kärnans uppgifter ut ur kärnan.

a) Det står att man "flyttat ut delar av kärnans uppgifter ut ur kärnan". Vart har man flyttat dem?

Svar:

Till vanliga processer ("user mode processes") som inte kör med de särskilda rättigheter som kärnan har ("kernel mode") utan i "user mode", och i vanligt minne ("user space").

b) Varför vill man göra det, dvs vilka fördelar ger en mikrokärna?

Svar:

En fördel med mikrokärnor är att om en av de funktioner som är placerade i vanliga processer utanför kärnan skulle krascha, så leder det inte till att hela systemet kraschar. Därigenom blir operativsystemet stabilare.

Eftersom det blir mindre kod i kärnan, blir det också lättare att granska och testa den, vilket är tänkt att göra operativsystemet stabilare (mot krascher) och säkrare (mot angrepp).

c) Vilka nackdelar ger det?

Svar:

Prestanda kan bli sämre, eftersom kärnan och de utflyttade delarna ibland måste kommunicera med varandra, och eftersom de utflyttade delarna kör i user mode och kärnan (förstås) i kernel mode, kan det behövas byten mellan user mode och kernel mode, och tillbaka, och kopiering av data mellan user space (användarprocessernas minne) och kernel space (kärnans minne).

d) Ge exempel på någon uppgift som man kan flytta ut ur kärnan.

Svar:

Användargränssnitt. Hantering av filsystem.

e) Ge exempel på någon uppgift som man inte kan flytta ut ur kärnan. Varför går det inte att flytta ut den?

Svar:

Skapande, schedulering och annan hantering av processer. Dessa kräver tillgång både till priviligierade instruktioner som processorn bara tillåter i kernel mode, och till kärnans interna datastrukturer (till exempel taskstructar och köer).

Uppgift 3 (5 p)

Här är ett C-program för Linux.

#include <stdio.h>
#include <unistd.h>

int x = 0;

int main(void) {
    int y = 0;
    fork();
    x = x + 1;
    y = y + 1;
    fork();
    x = x + 1;
    y = y + 1;
    fork();
    x = x + 1;
    y = y + 1;
    printf("x = %d, y = %d\n", x, y);
}

a) Vad skrivs ut när programmet körs?

Svar:

x = 3, y = 3
x = 3, y = 3
x = 3, y = 3
x = 3, y = 3
x = 3, y = 3
x = 3, y = 3
x = 3, y = 3
x = 3, y = 3

b) Kan det bli olika utskrifter från gång till gång, och vad beror det i så fall på?

Svar:

Nej. Vid fork får barnprocessen en egen adressrymd, som till att börja med är en kopia av föräldraprocessens, förutom att returvärdet från fork-anropet är olika. Varje process har alltså sin egen adressrymd, så ingen av variablerna x och y är delad mellan dem. Raderna som skrivs ut kan visserligen komma i olika ordning, eftersom det inte finns någon synkronisering mellan de olika processerna, men i slutet av programmet när variablerna skrivs ut har de samma innehåll i alla processerna, så utskrifterna är identiska.

Det kan förstås bli olika utskrifter, till exempel om någon stänger av datorn medan programmet körs, eller om datorn redan kör maximalt antal processer, så att fork-anropen misslyckas, eller om kosmisk strålning påverkar innehållet i minnet.

Uppgift 4 (5 p)

Här är ett annat C-program för Linux.

#include <stdio.h>
#include <pthread.h>

volatile int x = 0;

void *thread_body(void *arg) {
    int y = 0;
    x = x + 1;
    y = y + 1;
    printf("x = %d, y = %d\n", x, y);
    return NULL;
}

int main(void) {
    int z = 0;
    x = x + 1;
    z = z + 1;
    printf("x = %d, z = %d\n", x, z);
    pthread_t thread1, thread2;
    pthread_create(&thread1, NULL, thread_body, NULL);
    pthread_create(&thread2, NULL, thread_body, NULL);
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    printf("x = %d, z = %d\n", x, z);
}

a) Vad skrivs ut när programmet körs?

Svar:

Det här är det resultat man kan förvänta sig:
x = 1, z = 1
x = 2, y = 1
x = 3, y = 1
x = 3, z = 1
Vid en provkörning en miljon gånger fick man ovanstående resultat 999844 gånger. Man fick följande resultat 136 gånger:
x = 1, z = 1
x = 3, y = 1
x = 2, y = 1
x = 3, z = 1
Det blev detta resultat 20 gånger:
x = 1, z = 1
x = 2, y = 1
x = 2, y = 1
x = 2, z = 1
Vid en annan provkörning fick man också detta resultat:
x = 1, z = 1
x = 3, y = 1
x = 3, y = 1
x = 3, z = 1

b) Kan det bli olika utskrifter från gång till gång, och vad beror det i så fall på?

Svar:

  • Ja, värdena på x kan bli olika. Variabeln x är delad mellan trådarna, och det finns ingen synkronisering mellan trådarna. Båda trådarna uppdaterar variabeln x, som är gemensam för båda trådarna. Därför kan innehållet i x bli olika mellan olika körningar av programmet, både till slut och i trådarna.
  • Värdena på y och z kan dock inte bli olika. Varje tråd har en egen stack, och en egen variabel y, så utskrifterna av y är alltid lika. Variabeln z finns bara i main-funktionen, som körs i huvudtråden, så den finns bara i ett exemplar, och utskrifterna av z är alltid lika.
  • Ordningen på utskrifterna i thread_body kan också bli olika, men det påverkar inte hur utskrifterna ser ut. Värdet på x kan som vi sett bli olika mellan trådarna, men själva ordningen på utskrifterna spelar ingen roll. Om man hade haft ett trådnummer i varje tråd, och skrivit ut det trådnumret i utskriften inuti tråden, så hade man sett i vilken ordning utskrifterna kom, och det hade man sett även om det var samma värde på x.

Uppgift 5 (7 p)

Som ett hobbyprojekt ska vi bygga en dator från grunden, med grindar ("gates") och transistorer i stället för en färdig processor. Datorn ska vara byteadresserad och ha virtuellt minne. Fysiska adresser består av 6 bitar, och virtuella adresser av 8 bitar. Offset är 4 bitar.

a) Hur stor är den virtuella adressrymden?

Svar:

28 adresser, dvs 256 adresser eller 256 byte

b) Hur stor är den fysiska adressrymden?

Svar:

26 adresser, dvs 64 adresser eller 64 byte

c) Hur stor är en virtuell minnessida ("page" på engelska)?

Svar:

24 byte, dvs 16 byte

d) Hur stor är en fysisk frame ("ram" på svenska)?

Svar:

Lika stor som en virtuell minnessida, dvs 16 byte

e) Hur många frames (dvs platser för minnessidor) kan man ha i datorn?

Svar:

26 / 24 = 26-4 frames, dvs 4 frames

f) Vi tänker oss att vi har en sidtabell ("page table") för en process, och att enligt den sidtabellen är virtuell minnessida 3 lagrad i fysisk frame 2. (Båda numreras med början på noll.) Ange en adress inom denna virtuella minnessida, och visa hur den översätts till en fysisk adress. Visa både hur översättningen görs och vad resultatet blir.

Svar:

3 decimalt är 11 binärt, och den första adressen på den virtuella minnessidan 3 (dvs den fjärde) är 0011 0000 binärt, eller 3*16 = 48 decimalt. Via sidtabellen översätter vi sidnummer 3 (11 binärt) till framenummer 2 (10 binärt), och ersätter 3 (0011 binärt) i den virtuella adressen med framenummer 2 (10 binärt): 10 0000 binärt, dvs 2*16 = 32 decimalt.

På samma sätt översätts virtuell adress 49 (0011 0001 binärt) till 33 (10 0001 binärt), virtuell adress 50 (0011 0010 binärt) till 34 (10 0010 binärt), och så vidare till den sista virtuella adressen på den sidan, 63 (0011 1111 binärt), som översätts till 33 (10 1111 binärt):

Virtuell adress Fysisk adress
Decimalt Binärt Decimalt Binärt
48 0011 0000 32 10 0000
49 0011 0001 33 10 0001
50 0011 0010 34 10 0010
51 0011 0011 35 10 0011
52 0011 0100 36 10 0100
53 0011 0101 37 10 0101
54 0011 0110 38 10 0110
55 0011 0111 39 10 0111
56 0011 1000 40 10 1000
57 0011 1001 41 10 1001
58 0011 1010 42 10 1010
59 0011 1011 43 10 1011
60 0011 1100 44 10 1100
61 0011 1101 45 10 1101
62 0011 1110 46 10 1110

g) Om vi vill använda den här datorn till något, som till exempel beräkningar, vilka problem kommer vi att få?

Svar:

Minnet är förstås mycket litet, så det går inte att göra särskilt komplicerade beräkningar. Dels är det virtuella minnet litet, så vi kan inte ha så mycket data, och dessutom är det fysiska minnet ännu mindre, så om vi har annat än mycket lite data kommer vi att behöva page:a ut och in från någon sorts sekundärminne, vilket blir långsamt. Om vi dessutom tänkt oss att ha körbar programkod, operativsystem och sidtabell i primärminnet, blir det verkligen svår att få plats med allt. (Men våra 256 byte virtuellt minne är mer minne än vad som från början fanns i ENIAC: 20 stycken register för 10-siffriga decimala tal!)

Minnet är litet, men sidstorleken är stor jämfört med minnet. Det kan ge onödigt stor intern fragmentering. (Intern fragmentering betyder att eftersom minnessidorna har en fast storlek, kommer en del av vissa sidor att vara oanvänd.)

Det finns bara plats för 4 sidor åt gången i minnet, vilket kan orsaka thrashing. (Thrashing betyder att de data som används inte alla får plats i det fysiska minnet, så datorn måste ägna mycket tid åt att flytta data fram och tillbaka mellan primärminnet och sekundärminnet.) Särskilt illa blir det om vi tänkt oss att vi ska kunna köra flera processer samtidigt.

Uppgift 6 (3 p)

När man ska lagra de data som hör till en process kan det vara svårt att hitta ett tillräckligt stort, sammanhängande utrymme i det fysiska primärminnet. Hur löser man det problemet på vanliga moderna datorer och med vanliga moderna operativsystem som Linux och Windows?

Svar:

Med sidindelat virtuellt minne. Man allokerar inte sammanhängande fysiskt minne, utan använder en virtuell minnesrymd som delas in i lika stora "sidor" (på engelska "pages"), exempelvis med storleken fyra kilobyte. Det fysiska minnet delas in i "frames" (på svenska kanske "ramar"), med samma storlek som sidorna, och en sida kan sedan placeras i vilken ledig frame som helst. Processens sidor behöver inte lagras sammanhängande eller i ordning i ramarna. "Sidtabellen" (på engelska "page table") översätter mellan sidnummer och ramnummer, dvs mellan virtuella och fysiska adresser. Den virtuella minnesrymden är sammanhängande, men fysiskt kan sidorna vara lagrade i en annan ordning.

Man kombinerar ofta detta med "paging", som innebär att en sidas innehåll kan flyttas till sekundärminne om det inte används just då. Då kan man frigöra plats i det fysiska primärminnet, för innehåll som faktiskt ska användas. Det gör att en process kan använda mer minne än vad som fysiskt finns, och det underlättar att hitta lediga frames, men det som löser problemet med att hitta ett tillräckligt stort, sammanhängande utrymme är den sidindelade virtuella adressrymden, inte att minnesinnehåll sparas på sekundärminne.

Uppgift 7 (3 p)

Varje process har normalt en egen minnesrymd, medan flera trådar kan dela samma minnesrymd. Man kan också med särskilda systemanrop få flera processer att dela ett gemensamt minnesutrymme. I Unix och Linux kan man använda systemanropet shm_open för att skapa delat minne, och systemanropet mmap för att lägga in detta delade minne i den virtuella minnesrymden för en process.

Om man låter två processer dela på ett gemensamt minnesutrymme, är en del av minnet alltså gemensamt för de två processerna. Har de blivit två trådar nu? Eller finns det skillnader jämfört med om de verkligen var trådar? Förklara!

Svar:

Förutom det gemensamma minnesutrymmet har varje process fortfarande eget minne, som den andra processen inte kommer åt. Trådar lever inom en process och i dess minnesutrymme, och de olika trådarna har inget eget minne som de andra trådarna inte kan komma åt. Varje tråd har en egen stack, men den skyddas inte från åtkomst från de andra trådarna.

Operativsystemets data om processerna, som till exempel vilken användare det är som kör och vilka filer de har öppna, är fortfarande separata för de två processerna, inte gemensamma som om de varit trådar i samma process.

En annan skillnad mot om det var trådar är att trådar kan vara "user space-trådar", vars schedulering sköts helt av vanliga programkod i det körande programmet, utan att operativsystemkärnan är inblandad eller ens vet om att de finns. Det kan inte dessa processer.


Thomas Padron-McCarthy (thomas.padron-mccarthy@oru.se), 14 augusti 2023