Copia di https://etherpad.wikimedia.org/p/so.cs.unibo.it (25 febbraio 2019)

From Sistemi Operativi
Jump to navigation Jump to search
Sistemi Operativi 2018/19

Esercizi?
Shell Scripting?

---------------------------------
Lezione di venerdì 14 Dicembre 2018
#define MAXLEN ....
bbsend(dst, msg)
     static len=0
     asend(getpid(), TAG)
     while ((msgx = arecv(NULL)) != TAG)
             if (msgx == ACK) len--;
     while (len >= MAXLEN):
         msgx = arecv(NULL);
         if (msgx == ACK) len--;
    len++;
    asend(dst, msg)

bbsend(dst, msg)
     static len=0
     while (len >= MAXLEN):
         arecv(dst);
         len--;
    len++;
    asend(dst, msg)
    
bbrecv(sender)
    msg=arecv(sender)
    asend(sender, ACK)
    return msg

philo: process[i, i=0,...,4]
     asend(stick[i], (i, GET))
     arecv(stick[i])
     asend(stick[(i+1) % 5, (i,GET))
     arecv(stick[i+1])
     eat
    asend(stick[i], (i, PUT))
     asend(stick[(i+1) % 5, (i, PUT))


stick: process[i, i=0,...,4]
     status == IDLE
     queue = [] //coda dei processi in attesa di una risorsa
     while True:
         index, msg = recv(NULL)
         if (msg == PUT)
             status = IDLE
        else
             queue.enqueue(index)
         if (status == IDLE and queue.len > 0)
             status = BUSY
             index = queue.dequeue()
             asend(index, ACK)
             
             //crea deadlock

---------------------------------
Lezione di giovedì 13 Dicembre 2018

Nel modello a memoria privata i vari processi vivono in due mondi separati e comunicano tra di loro inviandosi messaggi.

Primitive:
- send, permette di inviare dati ad un determinato destinatario;
- receive, permette di ricevere dati da un determinato mittente, oppure da tutti i mittenti possibili.

Modalità di message passing:

- sincrona, entrambe le primitive solo bloccanti (es. chiamata telefonica);

    void ssend(pid_t dst, msg_t msg);

    msg_t sreceive(pid_t sender);


- asincrona, la send non è bloccante, la receive si, quindi la send manda quando vuole mentre la receive attende finchè non ci sono dati (es. posta elettronica);

    void  asend(pid_t dst, msg_t msg);

    msg_t   arecv(pid_t sender);


- completamente asincrona, entrambe le primitive sono non bloccanti, la send manda quando vuole, la receive se non ci sono messaggi non rimane in attesa.

    void nbsend(pid_t dst, msg_t msg);

    msg_t nbrecv(pid_t sender);


Da asincrono a sincrono:

    - problema: ho le primitive asend ed arecv, devo realizzare ssend e srecv;

    - soluzione: implemento un sistema di acknowledgment, nel quale il destinatario avverte della avvenuta ricezione del pacchetto e

    il mittente continua ad inviare fintanto che gli arrivano messaggi ACK.


    - implementazione:

    ssend(dst, msg): 

        asend(dst, (getpid(), msg)) //getpid() indica il processo mittente

        arecv(dst)

    srecv(sender):

        (real_sender, msg) = arecv(sender)

        asend(real_sender, 'ACK')

        return msg


    - side effects: se entrambi i processi invocano la ssend contemporaneamente si genera deadlock.


Da sincrono ad asincrono:

    - problema:  ho le primitive ssend ed srecv, devo realizzare asend e arecv;

    - soluzione: aggiungo un daemon (demone in UNIX, ovvero un processo che attenda e risponda a determinati eventi) lato server che mi faccia da tramite e

    che memorizzi in un buffer i dati che dal mittente devono arrivare al destinatario.


    - implementazione:

    asend(dst, msg):

    ssend(server, (getpid(), MSG, dst, msg)) //il tag MSG indica al server che questo è un messaggio contenente dati

    arecv(sender):

    ssend(server, (getpid(), OK2RECV, sender, NULL)) //il tag OK2RECV indica al server che il destinatario è pronto a ricevere

    msg=srecv(server)

    return msg


    daemon():

        waiting4[] = {NONE,....,NONE}

        while True:

            (s, tag, proc, msg) = srecv(NULL)

            switch tag:

                case MSG: buf[proc].add(msg)

                        wproc = waiting4[proc]

                        if wproc != NONE && (msg=buf[proc].get(wproc))!=NULL

                               ssend(proc, msg)

                               waiting4[proc] = NONE

                case OK2RECV:

                    if ((msg=buf[s].get(proc))!=NULL)

                           ssend(s, msg)

                    else

                           waiting4[s] = proc


    - side effects: non hanno lo stesso potere espressivo in quanto devo aggiungere un processo e non una libreria.


---------------------------------
Lezione di martedì 11 Dicembre 2018
// http://www.cs.unibo.it/~renzo/so/compiti/2013.09.12.tot.pdf
process utente[i] {
 while true {
 /* sporca biancheria */
 lavatrice.enter()
 /* lava biancheria */
 lavatrice.exit()
 }
}

monitor lavatrice:
        condition ok2wash
        int waitcount
        int washing
entry enter():
        if (waitcount < 2 || washing > 0)
                waitcount++;
                ok2wash.wait()
                waitcount--;
        washing++;
        if (washing < 5)
                ok2wash.signal()
entry exit():
        washing--;
        if (washing == 0 && waitcount >= 3)
                ok2wash.signal()


//filosofi

philo[i]: process i=0,...,4

    think

    dp.starteating(i)

    eat

    dp.endeating(i)


dp: monitor

    bool stick[5] // true se usata

    condition cphilo[5] // !stick[i] && !stick[(I+1) %5]

    entry starteating(i)

    if(stick[i] || stick[(i+1) %5])

        cphilo[i].wait()

    stick[i]=stick[(i+1) %5]=True


    entry endeating(i):

    stick[i]=stick[(i+1) %5]=False

    if (stick[(i+4) % 5) == False

    cphilo[(i+4) % 5].signal()

    if (stick[(i+2) % 5) == False

    cphilo[(i+1) % 5].signal()


s0 p0 s1 p1 s2 p2 s3 p3 s4 p4 s0
//no deadlock (tutte e due o nessuna)
//congiura dei filosofi

dp: monitor

    bool stick[5] // true se usata

    cond busy[5] // stick[i] == false


    entry starteating(i)

    if (stick[i]) busy[i].wait()

    stick[i] = True;

    if (stick[(i+1) %5]) busy[(i+1) %5]).wait()

    stick[(i+1) %5] = True


    entry endeating(i):

    stick[i]=stick[(i+1) %5] = False

    busy[i].signal()

    abusy[(i+1) %5].signal()


//non crea deadlock per la mutua esclusione dei monitor

semaphore mutex  //implementiamo i monitor con i semafori
stack urgent
queue condq[i]
enter():
    mutex.P()
private __exit():
    if ((s = urgent.pop()) != NULL)

        s.V()

    else

        mutex.V()

exit():

    __exit()

wait(i):
    s = new semaphore(0)
    condq[i].enqueue(s)
    __exit()
    s.P()
signal(i):
    if ((s = condq[i].dequeue()) != NULL)

        us = new semaphore(0)

        urgent.push(us)

        s.V();

        us.P();


x=1 y=1 z=1
sem sp(0), sq(0),sr(0),ss(1);
Process P:
   while(1):
     sp.P()
     x = x+1;
     sq.V()

Process Q:
   while(1):
     sq.P()
     x = x+1;
     sr.V();
      
Process R:
   while(1):
     sr.P()
     y = y+x;
     ss.V()
      
Process S:
   while(1):
     ss.P()
     if (z==y):
        print(z)
        z=0;
        sp.V()
     else:
        z=z+1;
        ss.V()

---------------------------------
Lezione di giovedì 6 Dicembre 2018


    monitor semaphore: //implementiamo i semafori con un monitor

    int value
    condition C // value > 0
    
    entry P():
        if (value == 0) C.wait()
        value--
    entry V():
        value++
        C.signal()
    semaphore(X)

        value=X


monitor RW: //lettori e scrittori
    int nr;
    int nw;
    int ww; //waiting writers
    condition ok2read // nw == 0
    condition ok2write // nr == 0 && nw == 0
    
    entry beginr:
      if (nw != 0 || ww > 0) ok2read.wait()
      nr++
      ok2read.signal()
    entry endr:
      nr--
      if (nr == 0) ok2write.signal()
    entry beginw:
      if (nr != 0 || nw != 0) {ww++; ok2write.wait(); ww--}
      nw++
    entry endw:
      nw--
      ok2read.signal()
      if (nr == 0) ok2write.signal()


----- versione precedente
monitor RW: //lettori e scrittori
    int nr;
    int nw;
    condition ok2read // nw == 0
    condition ok2write // nr == 0 && nw == 0
    
    entry beginr:
      if (nw != 0) ok2read.wait()
      nr++
      ok2read.signal() //attivazione in cascata dei lettori
    entry endr:
      nr--
      if (nr == 0) ok2write.signal()
    entry beginw:
      if (nr != 0 || nw != 0) { ok2write.wait() }
      nw++
    entry endw:
      nw--
      ok2read.signal()
      if (nr == 0) ok2write.signal()
//questo ^ ha starvation ^

buffer limitato (BB => Bounded Buffer)

process producer()

    while True:

    x = produce

    bb.put(x)


process consumer()

    while True:

    x = bb.get()

    consume(x)


Monitor bb:
    condition ok2put // len(buffer) < SIZE
    condition ok2get // len(buffer) > 0
    queue buffer;
    entry put(int x):
        if (! (len(buffer) < SIZE)) ok2put.wait()
        buffer.enqueue(x)
        if (len(buffer) < SIZE) ok2put.signal()
        if (len(buffer) > 0) ok2get.signal()
    
    entry get():
        if (! len(buffer) > 0) ok2get.wait()
        rv = buffer.dequeue()
        if (len(buffer) < SIZE) ok2put.signal()
        if (len(buffer) > 0) ok2get.signal()
        return rv

Ottimizzato:
Monitor bb:
    condition ok2put // len(buffer) < SIZE
    condition ok2get // len(buffer) > 0
    queue buffer;
    entry put(int x):
        if (! (len(buffer) < SIZE)) ok2put.wait()
        buffer.enqueue(x)
        ok2get.signal()
    
    entry get():
        if (! len(buffer) > 0) ok2get.wait()
        rv = buffer.dequeue()
        ok2put.signal()
        return rv

class Monitor
{

    //Constructor

    //Properties

    int SIZE;


    Queue SharedBuffer; //zona di memoria condivisa

    Stack UrgentStack;


    //Procedure entries (funzioni visibili all'esterno)

    entry F1(...);


    //Condition variables (espressione booleana sullo stato del sistema)

    class Condition

    {

    Wait(); //blocca l'esecuzione di un processo, mettendo il processo nella coda della condizione

    Signal(); //risveglia l'esecuzione del primo processo bloccato, mettendo il processo corrente nello urgentStack e togliendo dalla coda della condizione il primo processo in attesa

    }


    Condition C1;

    Condition C2;

    ...

}

beginr():
        mutex.P()
        if (!nw == 0 || !wq.empty())
                        //non entra nemmeno se uno scrittore è in attesa, prima o poi il numero di lettori diventerà nullo
                s = new(sem(0))
                rq.enqueue(s)
                mutex.V()
                s.P()
                free(s)
        }
        nr++
        if (!rq.empty())
          s = dequeue(rq)
          s.V()
        else
          mutex.V()
}

endr():
        mutex.P()
        nr--
        if (nr==0 && !wq.empty())
          s = dequeue(wq)
          s.V()
        else
          mutex.V()
          
beginw():
        mutex.P()
        if (!(nr == 0 && nw == 0))
                s = new(sem(0))
                wq.enqueue(s)
                mutex.V()
                s.P()
                free(s)
        }
        wlast = true
        nw++
        mutex.V()
          
endw():
        mutex.P()
        nw--
        if (!rq.empty())
          s = dequeue(rq)
          s.V()
        else if (!wq.empty())
          s = dequeue(wq)
          s.V()
        else
          mutex.V()
          

          
----prev
beginr():
        mutex.P()
        if (!nw == 0)
                s = new(sem(0))
                rq.enqueue(s)
                mutex.V()
                s.P()
                free(s)
        }
        nr++
        nuovostato()
}

endr():
        mutex.P()
        nr--
        nuovostato()

beginw():
        mutex.P()
        if (!(nr == 0 && nw == 0))
                s = new(sem(0))
                wq.enqueue(s)
                mutex.V()
                s.P()
                free(s)
        }
        wlast = true
        nw++
        nuovostato()

endw():
        mutex.P()
        nw--
        nuovostato()

nuovostato():
        if (nw == 0 && !rq.empty())
          s = dequeue(rq)
          s.V()
        else if (nw==0 && nr==0 && !wq.empty())
          s = dequeue(wq)
          s.V()
        else
          mutex.V()

---------------------------------
Lezione di giovedì 29 Novembre 2018

beginr():
        mutex.P()
        if (!nw == 0) {
                s = new(sem(0))
                rq.enqueue(s)
                mutex.V()
                s.P()
                free(s)
        }
        nr++
        nuovostato()
}

endr():
        mutex.P()
        nr--
        nuovostato()

beginw():
        mutex.P()
        if (!(nr == 0 && nw == 0))
                s = new(sem(0))
                wq.enqueue(s)
                mutex.V()
                s.P()
                free(s)
        }
        nw++
        nuovostato()

endw():
        mutex.P()
        nw--
        nuovostato()

nuovostato():
        if (nw == 0 && ! rq.empty())
          s = dequeue(rq)
          s.V()
        else if (nw==0 && nr==0 && !wq.empty())
          s = dequeue(wq)
          s.V()
        else
          mutex.V()
          
          
(Semafori, prod/cons, Buffer Limitato, Sem Binari).

(Cena Filosofi, RW)

---------------------------------
Lezione di martedì 27 Novembre 2018

nP: numero di operazioni P completate
nV: numero di operazioni V completate
nP <= init + nV
init + nV - nP >= 0
init + nV - nP valore del semaforo

Il valore del semaforo è interno quindi non esiste una primitiva per leggerlo 


   class semaphore: 
        int value;
        queue_of_proc Q 
        semaphore(x): valure=x 
        
        P():  csenter()

         if valuee >0: 

    value--;

    else{

    this.process=ready.dequeue( );

    self.Q.enqueue(thisprocess);

    }

    csexit()

       V(): 

    csenter()

    if (len(Q) > 0){ 

    p = self.Q.dequeue()

    ready.enqueue(p)

    }else{

    value++;

    }


    csexit()


Problema Produttore-Consumatore con i semafori
    buffer buf 
    semaphore empty(BUFSIZE)
    semaphore full(0)
    semaphore mutex(1)             // serve per mutua esclusione
    
    producer: process

    int x 

    while(1) {

     x=produce();

    empty.P();

    mutex.P();

    buf.enqueue(x);

    mutex.V();

    full.V();

    }


    consumer: process

    while(1){

    full.P();

    x=buf.dequeue();

    consume(x);

    empty.V()

    consume(x);

    }



Cena filosofi 
Soluzione problema cena dei filosofi con semaforo

semaphore chopstick[5]=(1,1,1,1,1)
philo: process(i) i=0...4 
                   think()
                   eat(i)

eat(i) {

    chopstick[MIN(i,(i+1)%5)].P()

    chopstick[MAX(i,(i+1)%5)].P()

    ...eat...                    

    chopstick[MAX(i,(i+1)%5)].V()

    chopstick[MIN(i,(i+1)%5)].V()

}

---------------------------------
Lezione di giovedì 22 Novembre 2018

Paradigma:

    - Definizione di strutture, costrutti, elementi del linguaggio ai quali associamo un significato

    - Necessario dimostrare necessità

    - Come implementare in sistemi reali 



- Semafori

    - Concetto inventato da Djikstra 62/63

    - Permette la gestione di risorse condivise tra processi


    - Un semaforo è un intero protetto e condiviso tra i processi che facilita e limita l'accesso a risorse condivise in un ambiente con più processi attivi

    - Numero contenuto nel semaforo rappresenta il numero di risorse di un certo tipo disponibili ai processi. 

    - Caso particolare, semaforo binario. Unici valori possibili 0/1 (Mutex)


    - Operazioni sui semafori:

    - V, verhogen(aumentare):

    Semaforo incrementato. Se ci sono processi in coda, uno dei task in coda viene tolto dalla coda e posto in stato di ready

    - P, proberen (verificare): 

    Semaforo decrementato. Se il semaforo ha un valore negativo, il processo viene sospeso e accodato, in attesa di essere riattivato


Invariante del semaforo: 

    init <- valore iniziale 

    nP <- numero di operazioni di P completate

    nV <- numero di operazioni V completate 


Problema produttore-consumatore

    producer e consumer condividono un buffer comune. Compito del produttore è generare dati e depositarli nel buffer in continuo. Contemporaneamente, il consumatore utilizzerà i dati prodotti, rimuovendoli di volta in volta dal buffer. 

    Dobbiamo assicurarci che il produttore non elabori nuovi dati, se il buffer è pieno, e che il consumatore non cerchi dati, se il buffer è vuoto.


semaphore full(0);
semaphore empty(1);
share int global;

process producer:

    int i;

    while true:

    x = produce()

    empty.P();

    global = x;

    full.V();


process consumer;

    int x;

    while true:

    full.P()

    x = global;

    empty.V();

    consume(x);



-----------------------------------
Lezione di martedì 20 Novembre 2018

Python:

    #formattazione dell'output

    nome = "Renzo"

    eta = 25


    - .format(): "{0} è un giovanotto di {1} anni.".format(nome, eta) (disponibile dalla versione >= 2.6);

    - f-string: f"{nome} è un giovanotto di {eta} anni." (disponibile dalla versione >= 3.6).


C:

    - _Atomic: keyword che impone al compilatore di trattare l'oggetto denotato (e le operazioni su di esso) in maniera atomica;

    - volatile: keyword che impone al compilatore di trattare l'oggetto denotato come una vera a propria cella di memoria, senza nessuna "ottimizzazone" (es. uso di registri ausiliari) su di essa.

 
concorrenza: parallelismo apparente o reale (è indifferente per la trattazione teorica)
 
starting balance = 100
A: ld balance
A: add 10
B: ld balance
B: sub 10
B: st balance
A: st balance

Durante la sua vita un processo può essere in uno dei seguenti stati:
    - running se in esecuzione;
    - waiting/blocked se in attesa di un I/O o risorsa;
    - ready se ci sono tutte le condizioni per eseguirlo, ma in attesa di essere eseguito.

Evoluzione della vita di un processo 
->running

    - running->ready se "perde il turno" (preemption) 

    - ready->running se scelto dallo scheduler 

    - running->blocked se fa operazione bloccante 

    - blocked->ready se la condizione attesa si avvera....


Esempio di programmazione concorrente in pseudolinguaggio 
Process prova[i]: i=0..2   

    print('ciao %d', i)


 Process caio:  

    codice caio


Process sempronio: 

    codice sempronio


cobegin  

     .... codice processo 1 

    //

    .... codice processo 2 

coend
 ..dopo

Modelli della memoria:

    - privata: se più attori utilizzano aree di memoria diverse e per scambiarsi informazioni utilizzano dei "messaggi" -> message passing (processi);

    - condivisa: se più attori utilizzano la stessa memoria in maniera concorrente -> sincronizzazione (threading).


Casi d'uso della programmazione concorrente:

    - un singolo programma strutturato in più processi, ognuno con un proprio task;

    - programmi distinti collaborano tramite strutture dati (file condivisi)

    - programmi distinti condividono lo stesso ambiente di lavoro.


Proprietà di un programma sequenziale/imperativo:

    - liveness: arrivare;

    - safety: al risultato.


Proprietà di un programma concorrente/sincrono:

    - liveness: something eventually happens;

    - safety: nothing bad happens.


Safety:

    - Se un processo decide, decide uno dei valori proposti 

    - Se due processi decidono, decidono lo stesso valore 

Liveness: 

    - ogni processo corretto prima o poi decide


Nella programmazione concorrente:

    - Safety: 

    - NO RACE CONDITION 

    - NO DEADLOCK 

    - Liveness: 

    - Terminazione 

    - NO STARVATION


P: get(A) get(B) leave(A) leave(B) 
Q: get(B) get(A) leave(A) leave(B)  

H1: while(1) {get(A) leave(A)}    //   hi priority 
H2: while(1) {get(A) leave(A)}    //   hi priority 
L: while(1) {get(A) leave(A)}      // low priority 


Transazione atomica: 
una istruzione indivisibile di cui l'hardware può sempre garantirci l'esecuzione.
Singola operazione di macchina. L'atomicità di una operazione dipende dal tipo di hardware
es.Macchina RISC,tutte le istruzione hanno la stessa lunghezza

    -x=0 o x=100 operazioni atomiche (corrispondo allo store di 0 e 100)

    -x=40000000 è un'operazione troppo lunga per una singola istruzione e va eseguita in più parti, perdendo così di atomicità

Necessità di creare atomicità dove non c'è, definizione di sezione critica
PER NOI: 
    assegnamento di costante: ATOMICO 
    aggiornamento di variabile (incremento, valutazione di espressioni) NO. 

Sezioni critiche: 
metodo per rendere atomiche cose che non lo sono pendendo un blocco di comandi e rendendolo atomico
Nella nostra sintassi <...> rendono il blocco atomico.

Aggiunta al conto:
< load balance, sum 10, store balance >
csenter() ... csexit()

Rimozione dal conto:
< load balance, sub 10, store balance >
csenter() ... csexit()        

process P: i=1...N 

    while True:

    csenter()

    codice critico

    csexit()

    codice non critico


A* mutua esclusione:

    - 1 solo processo alla volta nel codice critico

B* assenza di deadlock:

    - Non si bloccano definitivamente in csenter

C* assenza di delay non necessari: 

    - Un processo che vuole eseguire codice critico non deve essere ritardato da uno che non vuole eseguire codice critico

D* assenza di starvation:                     

    - Quando un processo entra nella csenter prima o poi ne esce


DEKKER1 garantisce A, B, D, ma non C
DEKKER2 garantisce B, C, D, ma non A
DEKKER3 garantisce A, C, D, ma non B
DEKKER4 garantisce A, B, C, ma non D

Implementazione software delle sezioni critiche (Dekker, Peterson).
Implementazione hardware: spinlock.

Semafori...

-----------------------------------
Lezione di giovedì 15 Novembre 2018

Python:

    -Basi linguaggio

    -Shell interattiva

    -Interpretato (semicompilato automaticamente)


    - // divisione intera Python3

    - #  commento

    - # !/usr/bin/env python3, per rendere eseguibili  file.py 

    - eval(...) valuta come espressione il parametro passato, poco sicuro

    - doppio underscore rende attributi privati (__attribute)





-----------------------------------
Lezione di martedì 13 Novembre 2018

Segnale:

    - è una comunicazione dal kernel al processo che qualcosa è successo;

    - man 7 signal, comando per visualizzare la tabella completa dei segnali;

    - segnale : software = interrupt : hardware.


Segnali affidabili e non.
Segnali e fork/exec
funzioni re-entrant

Se durante l'esecuzione di un system-call lenta (es. lettura da terminale) viene invocato un segnale:
    - storicamente, il processo terminava restituendo uno speciale errore;
    - attualmente, il processo esce, gestisce il segnale e rientra.

signal set

sigaction:
    - (funzione) sceglie cosa fare quando viene comunicato un segnale, salvando lo stato attuale;
    - (struttura) continene le informazioni relative ad un' azione (handler, flags, mask, ...).
    
sigprocmask: permette di mascherare i segnali.
sigsuspend: al momento del raise del segnale, sospende l'esecuzione e applica la maschera scelta.
sigpending: ritorna una struttura contenente tutte i signali in attesa di essere gestiti.

pselect: attende per uno o più file descriptor di diventare pronti per operazioni di I/O.
poll: attende per un qualsiasi tipo di file descriptor o segnale.

new fd syscall: signalfd, eventfd, timerfd_create
wait3/wait4 and profiling

-----------------------------------
Lezione di giovedì 08 Novembre 2018

(fcntl)
file col buco...
mknod, pipe, mkfifo/named pipe.

select/poll
kill, signal

-----------------------------------
Lezione di martedì 06 Novembre 2018

uMPS2.

approfondimento su azioni atomiche dup2, APPEND mode, pread/pwrite

System call file system prefissi: L/F

System call file system: 

    -int stat (const char *file_name, struct stat *buf) restituisce informazioni sul file 

    -int access(const char *pathname, int mode) controllo se il chiamante può accedere al pathname 

    -mode_t umask (mode_t mask) imposta la maschera dei permessi dei file del processo del chiamante

    -mode_t chmod(const char *pathname, mode_t mode) change a files mode bits

    -int chown(const char *pathname, uid_t owner, gid_t group) cambia proprietario e gruppo del file  

    -int truncate(const char *path, off_t length) tronca un file ad una certa dimensione 

    -int link(const char *oldpath, const char *newpath) crea un nuovo hard link ad un file

    -int symlink(const char *path1, const char *path2) crea un nuovo symlink ad un file

    -int unlink(const char *pathname) elimina un nome del file 

    -int rename(const char *oldpath, const char *newpath), 

    -int mkdir(const char *path, mode_t mode), 

    -int rmdir(const char *path), 



chdir/fchdir (...at system call)

navigazione directory: getdents. (Lib: opendir/readdir/rewinddir/closedir)

-----------------------------------
Lezione di martedì 30 Ottobre 2018

Componenti di un sistema operativo
Gestione dei processi
Gestione della memoria principale
Gestione della memoria secondaria
Gestione file system
Gestione dei dispositivi di I/O
Protezione
Networking
(Interprete dei comandi)

System call gestione processi:

    -fork(...) duplica il processo in esecuzione. Ritorna 1 al padre e 0 al figlio (-1 in caso di errore)

    -wait(..) sospende il processo corrente finché un figlio termina o finché il processo corrente non riceve un segnale di terminazione 

    -waitpid(...) attente la terminazione del processo con pid passato alla funzione 

    -nice(..)  cambia valore di nice di un processo 

    (A process' nice value is a non-negative number for which a more positive value shall result in less favorable scheduling)


System call gestione memoria 

    -execvp(const char *file, char *const argv[])  esegue il programma puntato da file 

    Relative funz. di libreria

    -brk(void *addr), cambia la dimensione di un segmento di dati. Alloco/Dealloca la memoria di un processo


System call gestione file 

    -open(...) dato un pathname per un file ritorna un file descriptor

    file descriptor=numero intero che rappresenta un file, una pipe o un socket aperto da un processo per effettuare operazioni

    -close(...) chiude un file descriptor

    -read(int fd, void *buf, size_t count) legge da un file descriptor count bytes dentro il buffer buff

    -write(...) scrive su un file descriptor

    -pread(...) legge da un file descriptor con un certo offset

    -pwrite(...) scrive su un file descriptor con un certo offset

    -dup(...) duplica un file descriptor. La copia rimane sincronizzata con l’originale


Ridirezione

System call gestione I/O (ioctl)

    -ioctl(...) device specific input/output operations

    -fcntl(...) manipolazione del file descriptor. Può eseguire diverse operazioni a seconda del parametro comunque passato 



-----------------------------------
Lezione di giovedì 25 Ottobre 2018

SO: facilita' di uso, indipendenza dall'hardware, programmabilita',
  affidabilita'/solidita'.

Servizi (system call):
  gestione processi
  accesso I/O
  gestione errori
  interoperabilita' (IPC/networking)
  accounting
  protezione

Generazione 0: Babbage/Lady Ada/Menabrea
Generazione 1: valvole 45-55
  operatore = utente = costruttore = programmatore
Generazione 2: transistor 55-65
  primi linguaggi ad alto livello (Fortran, Lisp)

      Schede perforate.

  Batch.
  Definizione di Job
  Operatore != (programmatore = utente)
  Monitor (embrione di un Sistema Operativo)
Generazione 3: circuiti integrati 65 - 80
  Multiprogrammazione (avvicendamento per I/O)
  (interrupt per evitare polling)
  (gestione memoria)
  poi Time Sharing (quanto di tempo, necessita' dell'Interval Timer)
  -> sistemi interattivi.
  (gestione memoria, scheduling, protezione)
  esempi multics poi UNIX 1970.
  utenti != programmatori
Generazione 4: personal computer >80
  microprocessori -> sistemi personali
  facilita' d'uso
  (problemi di sicurezza).

Sistemi Paralleli SIMD/MIMD
  per i MIMD: tightly coupled/loosely coupled
  per i sistemi operativi: SMP, non SMP
Sistemi Real Time
  Hard/soft

Architettura.
  Macchina di von Neumann: 

    -dati programma e istruzioni da eseguire sono nello stesso spazio di memoria

    - bus complesso di fili paralleli ai quali sono collegate le unità

     
  Ciclo di CPU : fetch/decode/load operandi/execute/store (+controllo interrupt)

     -2 tipi di archihtetture

    -RISC, tutte le istruzioni hanno la stessa lunghezza

    -CISC, istruzione di lunghezza diversa, passate al livello microcodice

    RISC più efficiente rispetto a CISC, ma per fare le stesse operazioni ha bisogno di più istruzioni


  I modi del processore:

    - modo kernel: accesso completo a memoria e hardware

    - modo user: accesso solo alla sua memoria e a operazioni base

    modalità controllata da un bit sulla CPU

    nell'architettura intel i livelli di privilegi sono chiamati ring


  Comunicazione coi controllori dei device
  Interrupt

     -  il ciclo di una istruzione ha una fase in più di controllo per gli interrupt ( Non viene mai interrotto il ciclo di una istruzione)

     - più fili del bus per alzare tensione e comunicarlo al processore per eseguire delle operazioni specifiche (variano da processore a processore)  

     - interrupt fa saltare a riga specifica, necessario salvare i dati dell'istruzione corrente per riprendere dopo fine dell'interrupt


  Interrupt hardware vs Interrupt software (detti Trap)
  Gestione dell'Interrupt
  parte del processore: interrupt, alla fine instruzione correntesalva stato,
  cerca handler, salta handler
  parte del S.O. salva stato, gestione, (scheduler?) ripristino stato, ritorno controllo
 Una system call è un interrupt software generato dal processore per il codice in esecuzione, che salta così al gestore dell'interrupt che lo valuta
 System call sono gestite come errori
  interrupt multipli e nidificati

    - interrupt con priorità diverse

    - interrupt di priorità maggiore vengono eseguiti prima

  mascheramento degli interrupt (mono e multiprocessore)

     -cpu ha registro che consente di mascherarsi dagli interrupt


  Senza interrupt = Polling

    - polling = azione ripetuta

    esempio dalla vita: quello che fanno i bambini in macchina: siamo arrivati? siamo arrivati?

    esempio prima degli interrupt: la cpu chiede al processo: hai finito? hai finito? prima o poi risponderà sì


DMA (Direct Memory Access).

    - Operazioni di buffer fatte dal controller  

    - Migliora l'efficienza e semplifica la scrittura di SO


  Gerarchia di memoria
  Tipi di memoria: 

    - primaria

    (codice e dati delle elaborazioni correnti);

    (nanosecondo);

    - secondaria

    (attivazione esecuzioni e programmi);

    (millisecondo);

    - terziaria

    (lente e umane, esempio CD, DVD, chiavette USB);


concetto di Cache: località (se faccio un accesso è probabile che dopo poco accedo al memoria subito vicina)
Concetto generale di Cache (hw o sw)
  
  Protezione di memoria => MMU (Memory Management Unit)

    - alcuni registri accessibili e altri no

    - limita l'accesso a determinati registri e nel caso scaturisce una Trap call

    - fa in modo che in usermode siano visibili solo alcuni registri


  Casi semplici base/limite, cenni di funzionamento MMU moderne


  Multiprocessore/muticore
  System Call.
 La gestione dei tipi nelle librerie.

-------------------------------------
Lezione di martedì 23 Ottobre 2018

Il catalogo delle system call di UNIX...
File I/O
Device I/O
Memoria
Process mgmt
Networking
IPC

Ritorno alla teoria...
Breve storia dei sistemi anche operativi.
Richiami di architettura: von newmann, interrupt/trap DMA, gerarchia di memoria, cache...

-------------------------------------
Lezione di giovedì 18 Ottobre 2018

* utente
* programmatore (prog. utente)
* programmatore (sistema)
* amministratore
* sviluppatore kernel

Storia dei S.O.
librerie e system call...
librerie: statiche dinamiche (nm, ldd, 
libreria standard C

--------------------------------------
Lezione di martedì 16 Ottobre 2018

Ulteriori dettagli di C:
 static extern auto register
 preprocessore compilazione condizionale....
 inizializzazioni
 const
... collegamento con shell:
 parametri a linea comando (e _start).
 (environment)
 librerie statiche e dinamiche...
 execs-s2argv
 
 
 link fisici e simbolici
 ps/pstree kill processi foreground/background
 > >> quoting...

-------------------------------------
Lezione di giovedì 11 Ottobre 2018

installazione di GNU-Linux
gruppi lab...
UNIX: struttura standard del file system (ma cosa e' il file system?)
user/group
tipi di file...

strace


strcase

man

user/group
tipi di file...

-------------------------------------
Lezione di martedì 9 Ottobre 2018

gruppi (wiki&42, !!, mascheramento

struct vs union (+ passate per valore).
l'ampiezza di un unione è pari all'ampiezza del campo più grande
l'ampiezza di una struct é pari alla somma delle ampiezze dei campi

Git/autotools/cmake

-------------------------------------
Lezione di martedì 2 Ottobre 2018

Linguaggio C: prin_C_ples

Compiler startup
Esperimento: http://www.cs.unibo.it/~renzo/so/misc/portability3.tgz
Analisi esercizi di Lettura

-----------------------------------
Lezione di giovedì 27 Settembre 2018

prima definizione di sistema operativo
(astrazione-unificazione-gestione-controllo)

algoritmo - programma - processo

livelli - linguaggi - servizi - astrazioni

livello n+1
linguaggio n+1
livello n
linguaggio n

programmi utente
---
librerie
---system call + ISA
Sistema Operativo (kernel)
---
HW

linguaggio = (alfabeto, lessico, sintassi, semantica)

UNIX & C
everything is a file
(unix v3)
(CLI)
C-toolchain

data types (+void)
operators (+bit wise operators)
control statements
functions

(quine)