Uke 12 - Linux kildekode, nice, fork, effektivitet, Docker

Oppgaver til mandag 17. - fredag 21. mars

Vi skal innom mange temaer i denne uken og teste dem ut i praksis. Dette inkluderer et eksempel på hvordan man kan sette opp en container og bruke denne til å teste effektiviteten til forskjellige programmeringsspråk.

Pga tidlig eksamen har vi blitt nødt til å halvere omfanget av oblig 3 og sette innleveringsfristen en uke tidligere. Oppgavene denne uken som er merket med (Oblig), utgjør dermed de siste oppgavene i obligatorisk innlevering nummer 3. Oblig 3 består av alle oppgaver merket (Oblig) fra og med uke 11 til og med uke 12 (altså kun 2 uker!) og har innleveringsfrist fredag 28. mars. Det er veldig viktig at denne obligen leveres i tide, da tirsdag 22. april er absolutt siste frist for oss å rapportere inn hvem som har fått godkjent alle obliger og kan gå opp til eksamen. Det betyr i praksis at dette må være klart før påske, det vil si fredag 11. april.

  1. Lag et script som tar en pid som argument og deretter går i en evig løkke og skriver ut antall ticks prosessen som har denne pid-en bruker i user-mode hvert sekund.

    Hint: hvis du leser manualsiden for proc vil du se at det finnes en side /proc/[number]/stat der [number] er en PID. Finn så fra manualsiden hvilke kolonner som gir tallene du er ute etter. Ticks omtales ofte også som jiffies under Linux. Se deretter hvordan en kommando som

    cat /proc/2192/stat | cut -d' ' -f 2
    kan plukke ut verdier fra kolonner.

  2. Man kan installere kildekoden for linux med
    sudo apt-get install linux-source
    
    Dette er gjort på data2500 og kildekoden ligger der i
    /usr/src/linux-source-5.10
    
    Finn ut fra filen
    /usr/src/linux-source-6.1/arch/x86/entry/syscalls/syscall_64.tbl
    
    hvor mange systemkall det er i denne versjonen av Linux-kjernen. I mappen over er det en fil som heter entry_64.S . Hvilken funksjon har koden i denne filen og hvilket språk er den skrevet i?
  3. Kommandoen scc (Succinct Code Counter) kan installeres med
    sudo snap install scc
    
    eller med en binærfil direkte lastet ned. Den er installert på data2500, utfør der følgende kommando:
    @data2500:~$ /snap/bin/scc /home/haugerud/linux-source-6.1/*
    
    (scc kan ikke kjøres utenfor /home, kjør det derfor på denne kopien. NB! Dette fungerer ikke om man ikke først kopierer denne mappen til sin egen mappe. Alternativt, se løsningsforslaget. Den vil etter noen sekunder gi litt statistikk om innholdet av Linux kildekoden (mens du venter på svar: Hvor mange språk tror du er representert og hva tror du er det mest brukte programmeringsspråket?): Hvis læreboken, Tanenbaum, har 40 linjer pr side og 1000 sider, hvor mange slike bøker vil Linux-kildekoden fylle? Og hvor stort er estimatet for hva det vil koste å utvikle denne koden?
  4. (Oblig) Start to regne-jobber på Linux VM. Bruk scriptet regn fra tidligere oppgaver, men legg til minst en null i løkken så de kjører lenger og kommenter bort litt av utskriften så det blir mindre utskrift i terminalen. Sjekk at de kjører med top. Hvor stor andel CPU-tid får de hver? Gi så den ene av prosessene så stor nice verdi som mulig, 19. Hvor stor andel CPU-tid får de nå hver? Hvis du ikke ser noen endring, hva kan det komme av? Hva om du starter tre regnejobber, ser du noen effekt av at en av dem har nice = 19? Hint: Sjekk 'Last Used Cpu' i top.
  5. (Oblig) Start to regne-jobber på din Linux-VM med følgende kommandoer:
    group70@os70:~$ taskset -c 0 ./regn&
    group70@os70:~$ taskset -c 0 ./regn&
    

    Forklar hva taskset gjør med prosessene. Sjekk gjerne at det stemmer ved å kjøre top, taste f, gå med piltaster ned til P = Last Used Cpu, taste space og så Esc. Hvor stor andel CPU-tid får de nå hver?

    Stopp så regn-jobbene og start to nye med
    group70@os70:~$ killall regn
    group70@os70:~$ taskset -c 0 ./regn&
    group70@os70:~$ taskset -c 0 nice -n 19 ./regn&
    
    Hvor stor andel CPU-tid får de nå hver?
  6. Ukens utfordring nr 1!

    Start igjen to regne-jobber på Linux VM med taskset slik at de kjører på samme CPU/kjerne og gi den ene av prosessene så stor nice verdi som mulig. Prøv så å gi den andre prosessen en så stor negativ nice-verdi som mulig. Ikke gi opp om du får permission denied, hvordan kan du få nok rettigheter til å få lov? (Dette fungerer desverre ikke lenger på Linux VMene(siden de egentlig er containere med reduserte rettigheter), men generelt trenger man sudo-rettigheter). Hvor stor andel CPU-tid får prosessene nå hver?
  7. Ukens utfordring nr 2!

    Prøv å finne ut (søk evnt. på nettet) hvordan Linux-kjernen fordeler CPU-tid ved hjelp av nice. Prøv å kjøre noen forsøk og se hvordan det stemmer med dine resultater.
  8. Studer og kjør følgende kode:

     
    #include <stdio.h>
    #include <sys/types.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>

    int main(){

    int i,id,pid,mypid,ppid;
    id = getpid();
    printf ("Fork demo!\n Jeg er parent-prosess med pid = %d \n",id);
    sleep(2);
    pid = fork();     /* Prosessen lager nå en ny uavhengig prosess
                         I den nye barne-prosessen er $pid = 0
                         I foreldre prosessen er $pid = barnets PID */
    if (pid == 0)
      {
        mypid = getpid();
        ppid  = getppid();
        printf("       Jeg er child (mypid = %d) av parent med pid = %d.\n",mypid,ppid);
        printf("       Here er variabelen pid = %d.\n",pid);
        for (i = 1;i < 10;i++)
          {
            printf("       %d \n",i);
            sleep(1);
          }
        printf("       Child prosess avslutter.\n");
        exit(0);
      } 
    else
      {
        sleep(2);
        printf("Parent %d venter på child %d her...\n",id,pid);
        wait(&pid); 
        printf("Child har avsluttet, Parent avslutter!\n");
      }

    }

    Vær oppmerksom på at etter fork()-kallet er det to uavhengige prosesser som kjører den samme koden! Eneste forskjell er at variabelen pid er forskjellig.

    Modifiser denne koden og lag et C-program som ved hjelp av fork() lager en child-prosess. Denne prosessen skal skrive ut sin egen og parent-prosessen sin PID, sove i 5 sekunder (sleep(5);) og deretter ved hjelp av fork() lage en ny child-prosess. Denne prosessen blir "grandchild" av den opprinnelige prosessen. Grandchild-prosessen skal skrive ut sin egen og parent-prosessen sin PID, sove i 5 sekunder og deretter avslutte. Alle prosesser skal vente på sine children og si ifra når de avslutter.

  9. Ta igjen utgangspunkt i fork.c. Endre koden slik at parent ikke venter og at child utfører
    ppid  = getppid();
    printf("       Jeg er child (mypid = %d) av parent med pid = %d.\n",mypid,ppid);
    
    rett før den avslutter. Hva kan det komme av at parent id nå har endret seg til pid = 1?
  10. (Oblig) Målet med denne oppgaven er å forberede en sammenligning av hastigheten til programeringsspråk for CPU-intensive programmer. I GitHub repositoriet https://github.com/haugerud/sum.git ligger det noen programmer skrevet i bash, C, Python, perl og java. Og en PHP-fil, men den kan du se bort ifra. Test at du kan klone dette til Linux VM med kommandoen git clone https://github.com/haugerud/sum.git slik at det lages en mappe med navn sum som inneholder kildekoden for programmer som i alle disse språkene utfører de samme CPU-intensive regneoperasjonene. Istedet for å installere kompilatorer og runtime miljøer for alle disse språkene i Linux-VM, skal du lage en Dockerfile som laster ned filene med git og installerer gcc, java og python i en Ubuntu-container. Du trenger å installere følgende:

    apt-get install build-essential
    apt-get install default-jdk
    apt-get install python3 
    
    Hvis du får en feilmelding av typen
    E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
    
    legg til --fix-missing når du kjører apt-get update i starten av Dockerfile. Det vil si, bruk:
     RUN apt-get --fix-missing -y update
    
    Hint: Hvis installasjonen spør interaktivt om tidssone og byggingen henger; Legg inn riktig tidsone som andre og tredje linje i Dockerfile:
    ENV TZ=Europe/Oslo
    RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime  && echo $TZ > /etc/timezone
    
    Etter at du har laget en Dockerfile som gjør dette og laster ned git-repositoriet sum, start containeren, gå inn i sum-mappen og sjekk at du kan kompilere og kjøre java-programmet med:
    javac Sum.java
    java Sum
    
    Og at du kan kompilere med gcc og kjøre C-programmet på vanlig måte. Vider skal du kunne kjøre
    ./sum.perl
    python3 sum.py
    

    Hvis du står fast på denne oppgaven, kan du se de tre videoene i ukens linux-forelesning som viser et forsøk på å løse denne og litt av de følgende oppgavene med samme tema. (de hadde den gang andre oppgavenummer).

  11. (Oblig) Nå skal du ta tiden på disse programmene inne i containeren du lagde i forrige oppgave. Du vil se at sum.bash er litt anderledes enn de andre. I dette programmet (eller scriptet) regnes det ut en sum 500.000 ganger. Alle de andre programmene regner ut denne summen TIMES ganger. Du måler tiden sum.bash bruker ved kommandoen
    root@33256e20c0ee:/sum# time ./sum.bash
    Ferdig, sum: 250000000000
    
    real	0m5.564s
    user	0m5.518s
    sys	0m0.037s
    
    Resultatet du bør se på er user + sys som vanligvis er det samme som real (for de andre programmene er stort sett sys = 0) og det brukte altså 5.5 sekunder (ett desimal holder).

    Hvis tiden real er mye større enn user + sys betyr det at andre programmer bruker mye CPU. Det kan innvirke på resultatet hvis den underliggende VM-serveren er tungt belastet, men den har 4 uavhengige CPUer, så det vil trolig stort sett ikke være et problem. Sett gjerne variabelen

    TIMEFORMAT="Real:%R User:%U System:%S %P%%"
    
    for å få et mer kompakt tidsresultat.

    Ved å bruke kommandoen time på de andre programmene skal du nå lage en tabell over hvor mange ganger raskere de andre programmene er enn sum.bash. Om de andre programmene ikke bruker 5.5 sekunder(eller den tiden du måler for sum.bash så kan du prøve å endre størrelsen på TIMES slik at programmet bruker ca like lang tid som sum.bash. Hvis f. eks. time sum.perl bruker 11 sekunder, så kan du halvere verdien på TIMES og prøve igjen, da bruker det trolig ca 5.5 sekunder. Etter å ha tilpasset TIMES slik at alle programmene bruker omtrent like lang tid som bash-scriptet, vil verdien på TIMES for et program si hvor mange ganger raskere det er enn bash-scriptet. Lag en tabell over hvor mange ganger raskere C, PHP, Python og java-programmene er enn bash-programmet. Sammenlign med resultatene fra en forelesning for noen år siden, listet nedenfor og kommenter.

    2010
    Språk CPU-tid i sekunder på cube TIMES
    bash 5.7 1
    php 5.6 21
    perl 5.6 75
    Java 5.6 13500
    C/C++ 5.6 63000

    2019
    sum.bash:    TIMES = 1;
    sum.perl:   $TIMES = 40;
    sum.py:      TIMES = 46
    sum.php:    $TIMES = 155;
    sum.c:   int TIMES = 4200;
    Sum.java:int TIMES = 15000;
    sumO.c:  int TIMES = 25000;
    
  12. Ukens største utfordring! Lag et script som kompilerer java og C, kjører og tar tiden på alle programmene og skriv om Dockerfile slik at dette scriptet blir kopiert til sum-mappen. Få også Dockerfilen til å lage en webserver og scriptet til å kjøre slik at resultatene vises i index.html på port 8181 når containeren startes på riktig måte. Klarer du å få dette til å kjøre på ubuntu 22.04 slik at resultatene kan sammenlignes? (kan bli plassproblemer om begge bygges samtidig?)
  13. Gjør nå tilsvarende som i oppgave 11 og 12 med regnejobber, men bruk istedet tar-filen nevnt under. Endre Dockerfilen slik at innholdet i denne istedet blir lastet inn, kompiler og kjør. Lag en ny katalog med mkdir tekst, gå til denne katalogen med cd tekst og last ned og pakk ut filene fra https://os.cs.oslomet.no/tekst.tgz. Dette er igjen programmer skrevet i bash, perl, PHP, java og C++(ikke C), men de utfører tekstbehandling på en stor fil. C++ programmet kompileres med g++ (apt-get install g++ hvis det ikke finnes). Se på fil.php eller noen av de andre programmene og prøv å finne ut hva de gjør. For at programmene skal virke som tenkt, må først filen stor kopieres til katalogen /tmp. Gjør dette med kommandoen cp stor /tmp. Når du får alle programmen til å kjøre og de gjør det de skal, kan du prøve å lage en tilsvarende liste som i forrige oppgave. Hvor mange ganger fortere enn fil.bash går de andre programmene? Hvis noen går for fort, kan du enten lage filen stor større eller lage en for-løkke slik at den samme operasjonen gjentas mange ganger.
  14. (Oblig) Dette er ukens OS-konkurranse.

    Strengen med 10 tegn som du skal finne ligger i en docker container som en gang har kjørt på din s-server. På containeren er det registrert en bruker med brukernavn os. Strengen du skal finne er relatert til hvilket passord denne brukeren må skrive for å kunne logge inn på containeren.

    Logg inn som tidligere med noe tilsvarende som følgende:

    $ ssh -p 5135 s135@intel3.vlab.cs.oslomet.no
    
    s135@intel3.vlab.cs.oslomet.no's password: 
    
    s135@os5135:~$ 
    

    For å sjekke at du har funnet det rette ordet, skriv strengen med 10 tegn inn på siden os.php uke 12 og du får beskjed om du har skrevet riktig. I tilegg blir du da med på konkurransen om å finne denne koden fortest mulig!