= Laboratorio di Sistemi Operativi = [[TOC(noheading)]] == Lezione 1: Introduzione == === Slide === * '''Turno 1 (4 Marzo 2009)''': [http://homes.dico.unimi.it/sisop/lucidi0809/solab01.pdf slide] ([http://homes.dico.unimi.it/sisop/lucidi0809/solab01-handout.pdf Versione stampa]) * '''Turno 2 (6 Marzo 2009)''': [http://homes.dico.unimi.it/sisop/lucidi0809/solab01b.pdf slide] ([http://homes.dico.unimi.it/sisop/lucidi0809/solab01b-handout.pdf Versione stampa]) === Programmi === * Accesso diretto alla macchina fisica `mioboot-nobios-simple.asm` {{{ bits 16 ; 16 bit real mode org 0x7C00 ; origine indirizzo 0000:7C00 start: mov ax, 0xb800 ; text video memory mov ds, ax mov eax, 10 write: cmp eax, 0 jz end mov byte [eax], 'm' mov byte [eax+1], 0x0F ; attrib = white on black sub eax, 2 jmp write end: hlt times 510-($-$$) db 0 ; 0-padding dw 0xAA55 }}} {{{ #!sh nasm -l mioboot-nobios-simple.lst -o mioboot-nobios-simple.img mioboot-nobios-simple.asm qemu mioboot-nobios-simple.img }}} * Uso dei servizi del BIOS `mioboot.asm` {{{ bits 16 ; 16 bit real mode org 0x7C00 ; origine indirizzo 0000:7C00 start: cld ; clears direction flag (index regs incremented) mov si, boot call message working: mov si, work call message call waitenter jmp working message: lodsb ; carica un byte da [DS:SI] in AL e inc SI cmp al, 0 jz done mov ah, 0x0E ; write char to screen in text mode mov bx, 0 ; BH page number BL foreground color int 0x10 ; write AL to screen (BIOS) jmp message done: ret boot: db "Loading unuseful system...." , 10, 13, 0 work: db "I've done my unuseful stuff!" , 10, 13, 0 cont: db "Hit ENTER to continue...", 10, 13, 0 wow: db "Great! Hello world!" , 10, 13, 0 waitenter: mov si, cont call message mov ah, 0 int 0x16 ; Wait for keypress (BIOS) cmp al, 'm' jz egg cmp al, 'b' jz basic cmp al, 13 jnz waitenter ret egg: mov si, wow call message jmp waitenter basic: int 0x18 ; basic (BIOS) hlt times 510-($-$$) db 0 dw 0xAA55 }}} {{{ #!sh nasm -l mioboot.lst -o mioboot.img mioboot.asm qemu mioboot.img # con qemu -d in_asm potete vedere (in /tmp/qemu.log) il codice eseguito dalla macchina virtuale: per la maggior parte istruzioni del BIOS }}} * Programma che esegue il codice letto da standard input exec.c {{{ #!c #include int main() { unsigned char buf[1024]; void (*ptr)(); int n; unsigned int eax; // leggi il codice da eseguire da standard input n = read(0, buf, 1024); // forza il codice a ritornare dalla chiamata a funzione (0xC3 = ret) buf[n] = '\xc3'; // esegui il codice letto come se fosse una funzione ptr = (void(*)()) buf; ptr(); // stampa il valore del registro EAX __asm__("mov %%eax, %0" : "=m"(eax)); printf("EAX: %.8x\n", eax); return 0; } }}} {{{ gcc -o exec exec.c echo -ne "\xB8\x11\x22\x33\x44" | ./exec echo -n "ciao" | ./exec ... }}} === Link === * [http://nasm.sourceforge.org NASM ] * [http://www.drpaulcarter.com/pcasm/ PC Assembly Language, by Paul A. Carter] * [http://www.intel.com/products/processor/manuals/ Manuali Intel] * [http://fabrice.bellard.free.fr/qemu_0.10.0-1_i386.deb QEmu] * [http://homes.dico.unimi.it/sisop/qemu-0.9.0-win32.zip QEmu 0.9.0 per windows] * [http://homes.dico.unimi.it/sisop/qemu_0.10.0-1_i386.deb QEmu 0.10 per Linux (Debian & Ubuntu)] * [http://homes.dico.unimi.it/~sisop/qemu/minix3-full.qcow Immagine Qemu MINIX 3.1.2a con Vim, ssh, ecc.] ---- == Lezione 2 == === Slide === * '''Turno 1 (11 Marzo 2009)''': [http://homes.dico.unimi.it/sisop/lucidi0809/solab02.pdf slide] ([http://homes.dico.unimi.it/sisop/lucidi0809/solab02-handout.pdf Versione stampa]) * '''Turno 2 (13 Marzo 2009)''': [http://homes.dico.unimi.it/sisop/lucidi0809/solab02b.pdf slide] ([http://homes.dico.unimi.it/sisop/lucidi0809/solab02b-handout.pdf Versione stampa]) === Esercizi === ==== {{{fork}}} ==== {{{ #!c /* un programma che "forca" un nuovo processo */ #include #include #include int main(void){ int x = fork(); if (x < 0){ perror("Errore nella fork:"); exit(1); } else { if (x != 0){ while(1){ sleep(1); printf("Processo padre (Figlio: %d)\n", x); } } else { while(1) { printf("Processo figlio (%d)\n", x); sleep(1); } } } return 0; } /* da compilare con un comando del tipo 'cc -o prova prova.c' */ /* Per terminare Ctrl-C */ }}} ==== Exit status ==== {{{ #!c #include #include int main(int argc, char **argv) { int i; if (argc != 2) return -1; return atoi(argv[1]); } /* da compilare con un comando del tipo 'cc -o prova prova.c' */ /* Per verificare l'exit status: ./prova ; echo $? */ }}} ==== Esecuzione in sequenza e parallelo ==== {{{ #!c #include #include int main(void){ int i = 0; for (i=0; i<10; ++i){ sleep(1); printf("%d: %d\n", getpid(), i); } return 0; } }}} {{{ #!sh # In sequenza ./hello ; ./hello # In parallelo ./hello & ./hello echo $? }}} ==== Cosa fa? ==== {{{ #!sh /usr/bin/touch piripacchio while /bin/ls piripacchio; do /usr/bin/sleep 2 /bin/echo ciao done & ( /usr/bin/sleep 10 ; /bin/rm piripacchio ) }}} Consultate il manuale! {{{ #!sh man man man touch man ls man 1 sleep # man 3 sleep รจ il manuale della funzione della libreria C man echo man rm }}} ==== Uso del {{{for}}} e {{{if}}} ==== {{{ #!sh for i in dog cat fish; do if ls /bin/$i; then echo Trovato $i; else echo $i non trovato; fi; done }}} {{{ #!sh man test for i in dog cat fish; do if test -f /bin/$i; then echo Trovato $i; else echo $i non trovato; fi; done }}} === Link === * [http://jrmiii.com/attachments/Vim.pdf Vi cheat sheet] * [http://tnerual.eriogerg.free.fr/vimqrc-it.pdf VIM: Scheda di riferimento rapido] * [http://homes.dico.unimi.it/~sisop/qemu/minix3-full.qcow Immagine Qemu MINIX 3.1.2a con Vim, ssh, ecc.] * [http://www.pluto.it/files/ildp/HOWTO/Bash-Prog-Intro-HOWTO/Bash-Prog-Intro-HOWTO.html BASH Programming -- Intoduction HOWTO] * [http://www.pluto.it/files/ildp/guide/abs/index.html Guida avanzata di scripting BASH] == Lezione 3 == === Slide === * '''Turno 1 (18 Marzo 2009)''': [http://homes.dico.unimi.it/sisop/lucidi0809/solab03.pdf slide] ([http://homes.dico.unimi.it/sisop/lucidi0809/solab03-handout.pdf Versione stampa]) * '''Turno 2 (20 Marzo 2009)''': [http://homes.dico.unimi.it/sisop/lucidi0809/solab03b.pdf slide] ([http://homes.dico.unimi.it/sisop/lucidi0809/solab03b-handout.pdf Versione stampa]) === Materiale === * [UnixShell UNIX Shell: kit di sopravvivenza (in aggiornamento)] ==== stdout.c ==== {{{ #!c /* scrive st stdout(1) gli argomenti ricevuti sulla riga di comando utile per capire come funzionano quoting, escaping e command substitution nella shell: e.s. ./stdout "ciao ciao" ./stdout ciao ciao a="giallo"; b="rosso" ; ./stdout "$a $b" ./stdout $(ls /etc) compilare con: cc -o stdout stdout.c */ #include int main(int argc, char **argv) { int i; for (i = 0; i < argc; i++) { printf("%d: %s\n", i, argv[i]); } return 0; } }}} ==== stdouterr.c ==== {{{ #!c /* scrive su stdout(1) e stderr (2) gli argomenti ricevuti sulla riga di comando; l'i-esimo argomento viene scritto su stdout se i e' pari e su stderr se i e' dispari utile per capire come funzionano redirezione e pipe nella shell e.s. ./stdouterr "primo argomento" "secondo argomento" | cat ./stdouterr "primo argomento" "secondo argomento" 2> /tmp/2 ./stdouterr "primo" "secondo" 2> /dev/null per compilare: cc -o stdouterr stdouterr.c */ #include #include int main(int argc, char **argv) { int i; char buf[2048]; for (i = 0; i < argc; i++) { snprintf(buf, 2047, "%d: %s\n", i, argv[i]); write(i % 2 + 1, buf, strlen(buf)); } return 0; } }}} === Esercizi online con la shell === * [http://idea.sec.dico.unimi.it/~lorenzo/SisOp0809/ Esercizi online] == Lezione 4 == === Slide === * '''Turno 1 (25 Marzo 2009)''': [http://homes.dico.unimi.it/sisop/lucidi0809/solab04.pdf slide] ([http://homes.dico.unimi.it/sisop/lucidi0809/solab04-handout.pdf Versione stampa]) === Programmi === ==== Esempi con la syscall `clone`, specifica di Linux ==== * Thread '''senza''' memoria condivisa `threads-isolated.c` {{{ #!c #include #include #include #include #include int run(void* s) { int* shared = (int*)s; /* alias per comodita` */ while (shared[0] < 10) { sleep(1); printf("Processo figlio (%d). s = %d\n", getpid(), shared[0]); if (!(shared[0] < 10)){ printf("Corsa critica!!!!\n"); abort(); } shared[0] += 1; } return 0; } int main(void){ int shared[2] = {0 , 0}; /* int clone(int (*fn)(void *), * void *child_stack, * int flags, * void *arg); * crea una copia del chiamante (con le caratteristiche * specificate da flags) e lo esegue partendo da fn */ if (clone(run, /* il nuovo * processo esegue run(shared), vedi quarto * parametro */ malloc(4096)+4096, /* lo stack del nuovo processo * (cresce verso il basso!) */ SIGCHLD, /* in questo caso la clone e` analoga alla fork */ shared) < 0){ perror("Errore nella creazione"); exit(1); } if (clone(run, malloc(4096)+4096, SIGCHLD, shared) < 0){ perror("Errore nella creazione"); exit(1); } /* Isolati: ciascuno dei figli esegue 10 volte. Per il padre * shared[0] e` sempre 0 */ while(1) { sleep(1); printf("Processo padre. s = %d\n", shared[0]); } return 0; } }}} * Thread '''con''' memoria condivisa `threads-shared.c` {{{ #!c #include #include #include #include #include int run(void* s) { int* shared = (int*)s; /* alias per comodita` */ while (shared[0] < 10) { sleep(1); printf("Processo figlio (%d). s = %d\n", getpid(), shared[0]); if (!(shared[0] < 10)){ printf("Corsa critica!!!!\n"); abort(); } shared[0] += 1; } return 0; } int main(void){ int shared[2] = {0 , 0}; /* int clone(int (*fn)(void *), * void *child_stack, * int flags, * void *arg); * crea una copia del chiamante (con le caratteristiche * specificate da flags) e lo esegue partendo da fn */ if (clone(run, /* il nuovo * processo esegue run(shared), vedi quarto * parametro */ malloc(4096)+4096, /* lo stack del nuovo processo * (cresce verso il basso!) */ CLONE_VM | SIGCHLD, /* la (virtual) memory e` condivisa */ shared) < 0){ perror("Errore nella creazione"); exit(1); } if (clone(run, malloc(4096)+4096, CLONE_VM | SIGCHLD, shared) < 0){ perror("Errore nella creazione"); exit(1); } /* Memoria condivisa: i due figli nell'insieme eseguono 10 o * 11 volte: e` possibile una corsa critica. Il padre * condivide shared[0] con i figli */ while(1) { sleep(1); printf("Processo padre. s = %d\n", shared[0]); } return 0; } }}} * Thread '''con''' memoria condivisa, mutua esclusione ottenuta con Peterson `threads-peterson.c` {{{ #!c#include #include #include #include #include void enter_section(int process, int* turn, int* interested) { int other = 1 - process; interested[process] = 1; *turn = process; while (*turn == process && interested[other]){ printf("Busy waiting di %d\n", process); } } void leave_section(int process, int* interested) { interested[process] = 0; } int run(const int p, void* s) { int* shared = (int*)s; /* alias per comodita` */ while (enter_section(p, &shared[1], &shared[2]), shared[0] < 10) { sleep(1); printf("Processo figlio (%d). s = %d\n", getpid(), shared[0]); if (!(shared[0] < 10)){ printf("Corsa critica!!!!\n"); abort(); } shared[0] += 1; leave_section(p, &shared[2]); } return 0; } }}} * Thread '''con''' memoria condivisa, mutua esclusione con TSL `threads-tsl.c`, `enter.asm` {{{ #!c #include #include #include #include #include void enter_section(int *s); /* in enter.asm */ void leave_section(int *s){ *s = 0; } int run(const int p, void* s) { int* shared = (int*)s; /* alias per comodita` */ while (enter_section(&shared[1]), shared[0] < 10) { sleep(rand() % 3); printf("Processo figlio (%d). s = %d\n", getpid(), shared[0]); if (!(shared[0] < 10)){ printf("Corsa critica!!!!\n"); abort(); } shared[0] += 1; leave_section(&shared[1]); } return 0; } int run0(void*s){ return run(0, s); } int run1(void*s){ return run(1, s); } int main(void){ int shared[4] = {0 , 0, 0, 0}; /* int clone(int (*fn)(void *), * void *child_stack, * int flags, * void *arg); * crea una copia del chiamante (con le caratteristiche * specificate da flags) e lo esegue partendo da fn */ if (clone(run0, /* il nuovo * processo esegue run(shared), vedi quarto * parametro */ malloc(4096)+4096, /* lo stack del nuovo processo * (cresce verso il basso!) */ CLONE_VM | SIGCHLD, /* la (virtual) memory e` condivisa */ shared) < 0){ perror("Errore nella creazione"); exit(1); } if (clone(run1, malloc(4096)+4096, CLONE_VM | SIGCHLD, shared) < 0){ perror("Errore nella creazione"); exit(1); } /* Memoria condivisa: i due figli nell'insieme eseguono 10 o * 11 volte: e` possibile una corsa critica. Il padre * condivide shared[0] con i figli */ while(shared[0] < 10) { sleep(1); printf("Processo padre. s = %d %d %d %d\n", shared[0], shared[1], shared[2], shared[3]); } return 0; } }}} {{{ section .text global enter_section enter_section: enter 0, 0 ; 0 bytes of local stack space mov ebx,[ebp+8] ; first parameter to function spin: lock bts dword [ebx], 0 jc spin leave ; mov esp,ebp / pop ebp ret }}} {{{ #!sh nasm -felf enter.asm cc threads-tsl.c enter.o -o threads-tsl }}} ==== Esempi in Java ==== * Creazione di thread `Basic.java` {{{ #!java class ClasseAttiva extends Thread{ public void run(){ while (true) { try { Thread.sleep(100); } catch(InterruptedException e){ System.err.println(e); } System.out.println(this.getName()); } } } public class Basic { public static final void main(final String[] args) { ClasseAttiva o1 = new ClasseAttiva(); ClasseAttiva o2 = new ClasseAttiva(); o1.start(); o2.start(); while (true){ try { Thread.sleep(100); } catch(InterruptedException e){ System.err.println(e); } System.out.println("Main thread"); } } } }}} * Memoria condivisa `Shared.java` {{{ #!java class ClasseAttiva extends Thread{ protected static int shared = 0; public void run() { while (true) { if (shared >= 10) break; try { Thread.sleep(1000); } catch(InterruptedException e){ System.err.println(e); } if (shared >= 10){ throw new Error("Corsa critica!!!"); } System.out.println(this.getName() + " s = " + shared); shared += 1; } } } public class Shared { public static final void main(final String[] args) { ClasseAttiva o1 = new ClasseAttiva(); ClasseAttiva o2 = new ClasseAttiva(); o1.start(); o2.start(); while (true){ try { Thread.sleep(1000); } catch(InterruptedException e){ System.err.println(e); } System.out.println("Main thread"); } } } }}} * Memoria condivisa, mutua esclusione ottenuta con `synchronized`, `Shared2.java` {{{ #!java class ClasseAttiva extends Thread{ protected static int shared = 0; protected static Object lock = new Object(); public void run() { while (true) { synchronized(lock){ if (shared >= 10) break; try { Thread.sleep(1000); } catch(InterruptedException e){ System.err.println(e); } if (shared >= 10){ throw new Error("Corsa critica!!!"); } System.out.println(this.getName() + " s = " + shared); shared += 1; } Thread.yield(); // non necessaria, ma favorisce lo scheduling di entrambi } } } public class Shared2 { public static final void main(final String[] args) { ClasseAttiva o1 = new ClasseAttiva(); ClasseAttiva o2 = new ClasseAttiva(); o1.start(); o2.start(); while (true){ try { Thread.sleep(1000); } catch(InterruptedException e){ System.err.println(e); } System.out.println("Main thread"); } } } }}} * Produttore e consumatore {{{ #!java class Actor extends Thread { public Actor(String nome){ super(nome); } private Magazzino shared; public final Magazzino getShared() { return shared; } public final void setShared(final Magazzino newShared) { this.shared = newShared; } } class Produttore extends Actor { public Produttore(String nome, Magazzino b) { super(nome); setShared(b); } public void run(){ int i = 0; while(true){ System.out.println(getName() + ": Inserisco " + i + " nel buffer"); getShared().put(i); i += 1; } } } class Consumatore extends Actor{ public Consumatore(String nome, Magazzino b) { super(nome); setShared(b); } public void run(){ while(true){ int i = getShared().get(); System.out.println(getName() + ": Estraggo " + i + " dal buffer"); } } } class Magazzino{ public final static int SIZE = 10; private int[] memory = new int[SIZE]; private int quanti = 0; public final int get() { String n = Thread.currentThread().getName(); synchronized(syncPC){ while(isVuoto()){ System.out.println(n + " ha tentato di leggere"); try { syncPC.wait(); } catch (InterruptedException e) { System.err.println(e); } } int ris = memory[--quanti]; if (quanti == SIZE - 1) syncPC.notifyAll(); System.out.println(n + " ha letto"); return ris; } } public final void put(final int newMemory) { String n = Thread.currentThread().getName(); synchronized(syncPC){ while (isPieno()){ System.out.println(n + " ha tentato di scrivere"); try { syncPC.wait(); } catch (InterruptedException e) { e.printStackTrace(System.err); } } memory[quanti++] = newMemory; if (quanti == 1) syncPC.notifyAll(); System.out.println(n + " ha scritto"); } } public final boolean isVuoto() { return quanti == 0; } public final boolean isPieno() { return quanti == SIZE; } private Object syncPC = new Object(); } public class PC { public static final void main(final String[] args) { Magazzino x = new Magazzino(); Produttore a1 = new Produttore("Aldo", x); Produttore a2 = new Produttore("Alberto", x); Consumatore b = new Consumatore("Barbara", x); a1.start(); b.start(); a2.start(); } } }}} * Produttore e consumatore con semafori `PCSem.java` {{{ #!java import java.util.concurrent.Semaphore; class Actor extends Thread { public Actor(String nome){ super(nome); } private Magazzino shared; public final Magazzino getShared() { return shared; } public final void setShared(final Magazzino newShared) { this.shared = newShared; } } class Produttore extends Actor { public Produttore(String nome, Magazzino b) { super(nome); setShared(b); } public void run(){ int i = 0; while(true){ System.out.println(getName() + ": Inserisco " + i + " nel buffer"); getShared().put(i); i += 1; } } } class Consumatore extends Actor{ public Consumatore(String nome, Magazzino b) { super(nome); setShared(b); } public void run(){ while(true){ int i = getShared().get(); System.out.println(getName() + ": Estraggo " + i + " dal buffer"); } } } class Magazzino{ public final static int SIZE = 10; private int[] memory = new int[SIZE]; private int quanti = 0; public final int get() { try{ String n = Thread.currentThread().getName(); pieno.acquire(); mutex.acquire(); int ris = memory[--quanti]; mutex.release(); vuoto.release(); System.out.println(n + " ha letto"); return ris; } catch(InterruptedException e){ System.err.println(e); return -1; } } public final void put(final int newMemory) { try{ String n = Thread.currentThread().getName(); vuoto.acquire(); mutex.acquire(); memory[quanti++] = newMemory; mutex.release(); pieno.release(); System.out.println(n + " ha scritto"); } catch(InterruptedException e){ System.err.println(e); } } private static final Semaphore mutex = new Semaphore(1); private static final Semaphore pieno = new Semaphore(0); private static final Semaphore vuoto = new Semaphore(SIZE); } public class PCSem { public static final void main(final String[] args) { Magazzino x = new Magazzino(); Produttore a1 = new Produttore("Aldo", x); Produttore a2 = new Produttore("Alberto", x); Consumatore b = new Consumatore("Barbara", x); a1.start(); b.start(); a2.start(); } } }}} * Produttore e consumatore con monitor `PCMon.java` {{{ #!java import java.util.concurrent.locks.*; class Actor extends Thread { public Actor(String nome){ super(nome); } private Magazzino shared; public final Magazzino getShared() { return shared; } public final void setShared(final Magazzino newShared) { this.shared = newShared; } } class Produttore extends Actor { public Produttore(String nome, Magazzino b) { super(nome); setShared(b); } public void run(){ int i = 0; while(true){ System.out.println(getName() + ": Inserisco " + i + " nel buffer"); getShared().put(i); i += 1; } } } class Consumatore extends Actor{ public Consumatore(String nome, Magazzino b) { super(nome); setShared(b); } public void run(){ while(true){ int i = getShared().get(); System.out.println(getName() + ": Estraggo " + i + " dal buffer"); } } } class Magazzino{ public final static int SIZE = 10; private int[] memory = new int[SIZE]; private int quanti = 0; public final int get() { monitor.lock(); int ris = -1; try{ String n = Thread.currentThread().getName(); while (isVuoto()){ System.out.println(n + " ha tentato di leggere"); empty.await(); } ris = memory[--quanti]; if (quanti == SIZE - 1) full.signal(); System.out.println(n + " ha letto"); } catch (InterruptedException e){ System.err.println(e); } finally { monitor.unlock(); return ris; } } public final void put(final int newMemory) { monitor.lock(); try{ String n = Thread.currentThread().getName(); while (isPieno()){ System.out.println(n + " ha tentato di scrivere"); full.await(); } memory[quanti++] = newMemory; if (quanti == 1) empty.signal(); System.out.println(n + " ha scritto"); } catch (InterruptedException e){ System.err.println(e); } finally { monitor.unlock(); } } public final boolean isVuoto() { return quanti == 0; } public final boolean isPieno() { return quanti == SIZE; } private static final Lock monitor = new ReentrantLock(); private static final Condition empty = monitor.newCondition(); private static final Condition full = monitor.newCondition(); } public class PCMon { public static final void main(final String[] args) { Magazzino x = new Magazzino(); Produttore a1 = new Produttore("Aldo", x); Produttore a2 = new Produttore("Alberto", x); Consumatore b = new Consumatore("Barbara", x); a1.start(); b.start(); a2.start(); } } }}}