Uke 14 - Threads, serialisering, race conditions, virtualisering og PowerShell.

Oppgaver til mandag 31. - fredag 4. apr

Aller først denne uken noen oppgaver hvor Java-tråder på Linux og Windows testes i praksis. Spesielt er det overraskende og se forskjellene i hvordan tråd-prioritet behandles med Linux og Windows. Dette gir et eksempel på at Java ikke er plattformavhengig i alle tilfeller. Deretter noen oppgaver om hvor galt det kan gå om man ikke serialiserer tråder som jobber med samme felles data. Siste del av oppgavesettet handler om virtualisering. Og som vanlig er det en konkurranse-oppgave helt til slutt.

Det er ikke flere obligatoriske innleveringer, så betrakt oppgaver som er merket (Oblig) som de oppgavene det er viktigst å få med seg i forbredelsene til eksamen.

  1. Oppgave 2.11 i Tanenbaum: Hva er den største fordelen med å implementere tråder i user space(at JVM selv schedulerer og ikke OS)? Hva er den største ulempen?
  2. Lagre filen Prior.java fra forelesningen på Linux-VM og kompiler og kjør den. Hvordan behandler Linux Java Virtual Machine (JVM = /usr/lib/bin/java) prioriteringen? Endre programmet slik at tråd to endrer prioritet til 4 istedet for 1. Ser dette ut til å gi noen endring i hvordan trådene blir prioritert? Eksperimenter med forskjellige prioriteter. Ser det ut til at noen av trådene får mer prioritet? Prøv å kjøre java med opsjonen -XX:ThreadPriorityPolicy=1 som vanlig bruker og som root med sudo java. Gir dette noen endring? Kommenter kort det du ser. (Les http://tech.stolsvik.com/2010/01/linux-java-thread-priorities-workaround.html for mer info om dette). Bruk taskset -c 0 hvis du kjører på en maskin med flere CPUer, ellers vil ikke trådene konkurrere om CPU-tid. NB! Linux-VM er egentlig en docker-container og man har noe begrensede rettigheter i forhold til om man kjører på en Linux server eller en Linux viruell maskin. Følgende
    root@os100:/home/group100# nice -n -5 ./regn
    nice: cannot set niceness: Permission denied
    
    viser at man ikke får lov til å sette negativ niceness som root som man normalt kunne gjort. Dette vil ha innvirkning på resultatet du får.
  3. Gjenta det samme med filen Prior.java fra forelesningen på Windows, eventuelt en Windows-VM. Hvordan behandler Windows Java Virtual Machine (JVM = java.exe) prioriteringen sammenlignet med Linux JVM? Endre programmet slik at tråd to endrer prioritet til 4 istedet for 1. Ser dette ut til å gi noen endring i hvordan trådene blir prioritert?
  4. (Oblig) I denne oppgaven skal vi ta utgangspunkt i programmet listet nedenfor. Hvis threads eller prosesser skal jobbe mot felles data, må man generelt synkronisere dem slik at de ikke modifiserer felles data samtidig; noe som har uforutsigbare konsekvenser. Programmet har en felles variabel static int saldo som de to trådene skal jobbe mot. Som vi har sett har Java en egen klasse Thread og for å lage egne threads kan du utvide denne klassen med extends. I main() lages nye tråder med
    SaldoThread s1 = new SaldoThread();
    
    En tråd settes i gang med s1.start(). Da startes run() som er en metode alle threads må ha. Variabler som defineres som static, er felles data for alle tråder.

    Studer først programmet "NosynchThread.java", som blir listet nedenfor, ved å lese programteksten (legg merke til at main også er en thread):

     
    // Kompileres med  javac NosynchThread.java
    // Run: java NosynchThread

    import java.lang.Thread;

    class SaldoThread extends Thread
    {
        static int MAX = 50000000;
        static int count = 0;
        public static int saldo; // Felles variable, gir race condition
        int id; 
        
        SaldoThread()
        {
            count++;
            id = count;
        }
        
        public void run()
        {
        System.out.println("Thread nr. "+ id +", med prioritet " + getPriority() + " starter");
        updateSaldo();
        }
        
        public void updateSaldo()
        {
        int i;
        if(id == 1) 
        {
            for(i = 1;i < MAX;i++) 
            {
            saldo++;
            }
        }
        else      
        {
            for(i = 1;i < MAX;i++)
            {
                saldo--;
            }
        }
        System.out.println("Thread nr. " + id + " ferdig. Saldo: " + saldo);
        }
    }

    class NosynchThread extends Thread
    {
       public static void main (String args[])
       {
           int i;
           System.out.println("Starter to threads!");

           SaldoThread s1 = new SaldoThread();
           SaldoThread s2 = new SaldoThread();
           s1.start();
           s2.start();

           try{s1.join();} catch (InterruptedException e){}
           try{s2.join();} catch (InterruptedException e){}

           System.out.println("Endelig total saldo: " +SaldoThread.saldo);
       }
    }

    Kompiler og kjør NosynchThread.java. Kan du forklare hva som skjer og det merkelige sluttresultatet for total saldo? Hvorfor endrer sluttresultatet seg fra gang til gang når det samme programmet kjøres omigjen? Gjør det noen forskjell på resultatet om du kjører programmet med
    $ taskset -c 0 java NosynchThread 
    
    Hva endres med hvordan trådene kjøres når du kjører java med taskset på denne måten? Det kan være du må gjenta taskset-kjøringen mange ganger (eventuelt øke max-verdien i loopen) for at du skal få 'gale' resultater. Hva er forskjellen på antall mulige "kollisjoner" når trådene kjører på samme CPU sammenlignet med når de kjører på hver sin CPU?
  5. Oppgave 2.19 i Tanenbaum. Hva er en race condition?
  6. (Oblig) Ta utgangspunkt i programmene thread.c, minimal.s og en.c fra forelesningen og lagre dem på data2500. Editer først thread.c og gang antall runder i løkken med en faktor 100, til en milliard, ellers vil du ikke se den ønskede effekten på data2500. Kompiler og kjør dem først med
    haugerud@data2500:~/lock$ gcc -pthread thread.c minimal.s 
    haugerud@data2500:~/lock$ taskset -c 0 ./a.out 
    
    Hvorfor får du nå alltid samme resultat selvom det er to uavhengige tråder som kjører?

    Gjenta kjøringen ved å istedet bruke en.c på følgende måte:

    haugerud@data2500:~/lock$ gcc -pthread thread.c en.c 
    haugerud@data2500:~/lock$ taskset -c 0 ./a.out 
    
    Kanskje må du kjøre den flere ganger for å se effekten, men hvorfor får du nå noen ganger forskjellig resultat? Kan det ha noe å gjøre med at kompilatoren kan lage flere linjer maskinkode av en linje høynivå-kode? Kompiler en.c på følgende måte:
    haugerud@data2500:~/lock$ gcc -c -S en.c
    
    Se så på den resulterende filen en.s og avgjør om flere linjer maskinkode kan være årsaken.

    Kompiler og kjør til slutt på følgende måte:

    haugerud@data2500:~/lock$ gcc -O -pthread thread.c en.c 
    haugerud@data2500:~/lock$ taskset -c 0 ./a.out 
    
    Hvorfor blir resultatet nå alltid det samme?

  7. Ukens nøtt: Ta utgangspunkt i C-programmet

     
    main(){
       int i,S = 0;
       for(i=1;i < 5;i++){
          S = S + i;
       }
    }

    kompiler det på en Linux host og finn ut hva som utgjør maskinkoden for main ved hjelp av objdump -d a.out. Prøv å finne ut hva denne koden gjør og hvorfor den er en korrekt oversettelse av for-løkken til maskinkode. Hvilken av høynivå variablene (i og S) tilsvarer -0x8(%rbp) og hvilken tilsvarer -0x4(%rbp)? Hvilket tall ligger lagret i -0x8(%rbp) når løkken har kjørt ferdig? Hint: $0x4 er tallet fire, -0x8(%rbp) er en referanse til et sted i minne der denne variabelen lagres, -0x4(%rbp) er en annen variabel, %eax er et register, og 80483d4 er et linjenummer.
  8. (Oblig) Dette var i fjor en nøtt, men i år skal du spørre Sikt-ChatGPT om hjelp, paste inn følgende spørsmål: Ta utgangspunkt i Java-programmet

     
    class For{
       public static void main(String args[]){
          int i,S = 0;
          for(i=1;i < 5;i++){
              S = S + i;
          }      
       }
    }

    kompiler det på en Linux host og finn ut hva som utgjør bytekoden gjør ved hjelp av javap -c For. Forklar hvorfor den er en korrekt oversettelse av for-løkken til bytekode. Hvilken av høynivå variablene (i og S) tilsvarer variable 1 og hvilken tilsvarer variable 2 (lagres der med istore_2). Hvilke tall ligger lagret i variable 1 og variable 2 når løkken har kjørt ferdig?
  9. (Oblig) Gi følgende spørsmål til Sikt-ChatGPT:

    Inntil 2005 var det slik at det fantes noen x86 instruksjoner (som for eksempel POPF) som bare ble oversett om de ble utført i user mode. Det disse instruksjonene utførte skulle bare være lov å gjøre i kernel mode og ingen ting skjedde når de ble utført i user mode. Forklar kort hva som gjorde dette problematisk i forbindelse med virtualisering og hvordan disse hardware-instruksjonene ble endret for å støtte x86-virtualisering.

    Sammenlign svaret med det som står skrevet om dette i forelesningsnotaten og vurder om du har fått et korrekt svar fra ChatGPT.
  10. (Oblig) På gruppens os-server, installer PowerShell for Linux med følgende kommandoer:
    # wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb"
    # dpkg -i packages-microsoft-prod.deb
    # apt-get update
    # apt-get install -y powershell
    
    Da vil du på os-serveren ved å kjøre kommandoen pwsh få opp nøyaktig samme PowerShell som vil bli tilgjengelig under eksamen:
    group131@os131:~$ pwsh
    PowerShell 7.5.0
    PS /home/group131>
    PS /home/group131> Get-ChildItem
    
        Directory: /home/group131
        
        UnixMode   User             Group                 LastWriteTime           Size Name
        --------   ----             -----                 -------------           ---- ----
        drwxrwxr-x group131         group131           04/17/2023 14:56           4096 j
        -rwx------ group131         group131           03/26/2023 22:38             33 a.sh
        
    
    Du kan gjøre de fleste av de kommende PowerShell-oppgavene med dette shellet og det kan være praktisk å kjenne til dette shellet da det pleier å være noen praktiske PowerShell-oppgaver til eksamen. Legg merke til at det er noen forskjeller fra et standard Windows PowerShell. I Windows er ls et alias for Get-ChildItem. Hva er kommandoen ls i pwsh? (hint: bruk Get-Command).
  11. (Oblig) Kjør følgende bash-script i en mappe på en Linux maskin og forklar kort hva resultatet blir:
        mkdir dir
        cd dir
        echo hei > fil.txt
        cp fil.txt lik.txt
        mv fil.txt ..
        cp lik.txt kopi.txt
        rm lik.txt
        ls
      
    Lag et PowerShell-script som gjør det samme som bash-scriptet, kjør det i PowerShell og sjekk at resulatet blir likt. Hvilke Cmdlets og functions bruker scriptet?
  12. (Oblig) Skriv ut verdien av systemvariabelen $PSVersionTable for å se hvilken versjon av powershell du kjorer.
  13. (Oblig) CreationTime er en property for et PowerShell filobjekt. Gi en PowerShell-kommando som gir hvilket tidspunkt filen fil.txt i mappen du står i ble laget. Hvis dere gjør oppgavene i pwsh-shellet i os-gruppen sin Linux-VM, er det viktig at man bruker de 'ekte' Cmdlet-ene som Get-Childitem og Get-Process og ikke ls og ps, fordi de sistnevnte da vil kjøre det underliggende Linux-kommandoene ls og ps og ikke PowerShell Cmdlet'ene.
  14. (Oblig) Gi en PowerShell-kommando som gir årstallet for når filen C:\Windows\system.ini (eller /etc/passwd i pwsh) ble laget.
  15. Gi en PowerShell-kommando som gir full path til filen fil.txt i mappen du står i.
  16. (Oblig) Gi en PowerShell-kommando som skriver ut prosess-IDen for prosessen med navn 'idle' (eller sshd i pswh). Bruk først Get-Member for å finne ut hvilke properties Cmdlet'en har.
  17. (Oblig) Start en notepad-prosess fra PowerShell. Bruk så en ps-metode som gjør at nodepad-prosessen stoppes . Bruk først Get-Member for å finne ut hvilke properties ps har. Lag en ny notepad-prosess og bruk aliaset kill til å drepe den. Hva skjer om du har to notepad-prosesser?
  18. Følgende kommando gir størrelsen på filen "test.txt" i antall bytes:
    (ls test.txt).Length
    
    Følgende kommando gir nøyaktig samme output:
    (ls test.txt).get_Length();
    
    Forklar forskjellen på disse kommandoene.
  19. (Oblig) Skriv et powershell-script som ved hjelp av en løkke skriver ut følgende:
    Linje 2
    Linje 4
    Linje 6
    Linje 8
    
  20. Skriv en powershell oneliner som dreper alle prosesser med navn notepad.
  21. (Oblig) I denne oppgaven som er relatert til plattformavhengighet skal du ta utgangspunkt i en fil som er spesiallaget kun for din s-gruppe. Filene for alle gruppene ligger på denne websiden. Man kan ikke liste filene på denne websiden, men for gruppe s133 ligger det der en kjørbar fil som heter p133 på denne websiden, tilsvarende for andre s-brukere. Pass på å velge filen som er til din s-gruppe og ikke til din os-gruppe! Hvis den kjøres på riktig plattform, gir den en 10-tegns kode med tilfeldige tegn som viser at du har løst oppgaven. Det er mulig å få tilgang til riktig plattform ved å laste ned riktig container til s-serveren din. Hvis det er for mange som samtidig prøver å laste ned samme type container, kan du få en beskjed om at det ikke går an å laste ned flere containere. (docker: Error response from daemon: toomanyrequests: You have reached your pull rate limit) Da er det mulig å prøve å laste ned containeren du ønsker fra public.ecr.aws/docker/library/navnet-til-containeren-du-vil-laste-ned istedet. For de utålmodige, kan det også være mulig å hacke seg frem til en løsning uten en container med den riktige plattformen, men det er et poeng med oppgaven at man skal kunne hente ned forskjellige platformer og kjøre dem. For å sjekke at du har funnet rette streng, skriv den inn på siden os.php uke 15 og du får beskjed om du har skrevet riktig. I tilegg blir du da med på konkurransen om å finne denne koden fortest mulig!