Costruzione di una semplice Agenda telefonica Elettronica Esercizio sull'uso delle principali system call Unix
Agenda telefonica 1.L'agenda e' costituita da un semplice file di testo. Ogni riga e' formata da: Cognome nome numero-telefonico ad esempio: Bollati Mario Corelli Simona Magni Giorgio
Comandi per gestire l'agenda cerca cognome –stampa sul terminale la riga del file agenda che contiene cognome inserisci cognome nome numero-telefonico –inserisce nel file agenda la riga formata da cognome, nome e numero di telefono rimuovi cognome –rimuove dall'agenda la riga contenente cognome
implementazione mediante demone e code di messaggi bassi mario Carli Bruno Massi Rino demone coda delle richieste di accesso all'agenda file "agenda" P2P3P1P4
Implementazione mediante demone e code di messaggi Il processo demone viene lanciato in back ground e resta in attesa sulla coda delle richieste di accesso all'agenda. L'accesso al file "agenda" e' gestito unicamente dal demone: le varie operazioni sono così automaticamente serializzate.
Implementazione mediante demone e code di messaggi le tre operazioni possono essere richieste al demone usando messaggi di tipo diverso (basta usare un numero diverso per ogni tipo di messaggio) il processo richiedente attende sulla coda la risposta del demone che restituisce il numero di telefono (per cerca) o semplicemente segnala il completamento dell'operazione (per inserisci e rimuovi)
Implementazione mediante demone e code di messaggi Come fare in modo che il demone comunichi direttamente ed esclusivamente con il processo che attende la sua risposta? Basta inserire nella stringa del messaggio il numero del processo richiedente: pid. Il demone risponde con un messaggio di tipo pid, che solo il processo stesso puo' ricevere.
Implementazione mediante semafori e accesso diretto al file "agenda" in modo mutuamente esclusivo bassi mario Carli Bruno Massi Rino file "agenda" P: V: P1P2P3P4 area ad accesso mutuamente esclusivo
Implementazione mediante semafori e accesso diretto al file "agenda" Le operazioni su "agenda" devono avvenire in mutua esclusione, attraverso l'uso di un semaforo binario inizializzato a 1 ognuno dei tre programmi, per accedere al file agenda deve prima eseguire una P sul semaforo, e poi puo' aprire il file e leggerlo o modificarne il contenuto. Alla fine delle operazioni sul file il processo deve eseguire una V sul semaforo prima di terminare.
Alcuni consigli Tenete tutti i programmi e il file agenda in un'unica directory. Come fanno i programmi a sapere l'identificatore della coda o semaforo da usare? Un modo semplice e' usare un file id_file per contere l'identificativo della coda o del semaforo: –nel caso della coda, e' il demone che preleva la coda e scrive il suo identificatore dentro id_file –Nel caso del semaforo, un programma ad hoc preleva il semaforo, scrive il suo identificatore dentro id_file e termina. I programmi leggono dentro id_file per conoscere l'identificatore da usare.
Alcuni consigli C'e' pero' un modo piu' elegante per far usare ad un insieme di programmi la coda (o il semaforo): msgid = msgget(KEY, IPC_CREAT | 666) KEY e' un numero intero concordato da tutti i programmi che vogliono usare la coda Alla prima chiamata della msgget con KEY, viene creata una nuova coda e il suo identificatore msgid associato biunivocamente a KEY. successivamente, qualsiasi altro programma che chiama msgget con lo stesso valore di KEY riceve il msgid associato a KEY.
Alcuni consigli ad esempio, in un file mydef.h possiamo mettere: #define LA_NOSTRA_CODA e in ogni programma che deve usare LA_NOSTRA_CODA: #include mydef.h..... msgid = msgget(LA_NOSTRA_CODA,IPC_CREAT|666); Notate: se 2 o piu' processi condividono lo stesso padre, allora si puo' fare anche in un altro modo: msgid = msgget(getppid(), IPC_CREAT|666);
Alcuni consigli quando avete finito di provare i programmi, ricordatevi di rimuovere le risorse prelevate (il semaforo o la coda) e, se presente, di uccidere il processo demone. Per compilare un programma C: gcc prog.c -o prog
Alcuni consigli potete usare solo "istruzioni C", se preferite, ma non scordatevi che puo' essere comodo usare anche i comandi shell. Ad esempio, per rimuovere la riga contentente cognome: $> grep –v cognome > agenda.tmp $> mv agenda.tmp agenda la system call system puo' essere usata per eseguire comandi shell da un programma C
per provare il funzionamento dei programmi : ovviamente lanciando direttamente i 3 comandi. potete pero' anche scrivere un programma che ciclicamente sceglie uno tra i comandi cerca inserisci e rimuovi e lo esegue. per produrre l'effetto di piu' richieste di accesso concorrente all'agenda, potete costruire un programma che si forka 2 o 3 volte, e ogni figlio esegue i comandi di uso dell'agenda.
consigli sul funzionamento del progr. di generazione delle richieste all'agenda: ogni figlio esegue un ciclo un numero finito di volte (ad esempio 100) oppure indefinitamente. ad ogni ciclo: –sleep un tempo random di 1 o 2 secondi –scegli in modo random uno dei tre comandi –esegui comando comando puo' essere eseguito con una exec o piu' semplicemente con una system
consigli sul funzionamento del progr. di generazione delle richieste all'agenda: il padre dei due figli che inviano ciclicamente le richieste puo' mettersi in attesa (wait) della loro terminazione, e poi rimuovere le risorse (se necessario uccidere il demone) e terminare. soprattutto nel caso in cui i figli ciclano all'infinito, potete inoltre prevedere un meccanismo di gestione della terminazione mediante signal e kill
terminazione dell'intero sistema mediante signal e kill il programma di generazione delle richieste all'agenda definisce una procedura di terminazione da eseguire alla ricezione di un segnale inviato da terminale mediante kill: $> kill –SIGUSR1 pid la procedura di terminazione rimuove le risorse (coda o semaforo) e termina il programma. ATTENZIONE: e' possibile far terminare anche i figli (e se necessario il demone)?
struttura generale del programma { termina(...) {...} signal(termina,SIGUSR1); fork() if figlio for (;;) { sleep(1,2); system(estrai,rimuovi,inserisci) } else fork() if figlio for (;;) {sleep(1,2); system(estrai,rimuovi,inserisci) } else {wait() }