vuoi
o PayPal
tutte le volte che vuoi
Implementazioni dei metodi Signal e Wait
Signal_and_wait (implementazione):
wait(){ condcount++; signal(mutex); wait(condsem); } signal() { condcount--; signal(condsem); wait(mutex); }
Signal_and_urgent_wait (implementazione):
wait() { condcount++; if (urgentcount > 0) signal(urgent); else signal(mutex); wait(condsem); condcount--; } signal() { urgentcount++; if (condcount > 0) { signal(condsem); wait(urgent); } urgentcount--; }
Signal_and_continue (implementazione):
wait() { condcount++; signal(mutex); wait(condsem); wait(mutex); } signal() { if (condcount > 0) { condcount--; signal(condsem); } } Signal_and_return (supponendo che la signal sia l'ultima operazione): wait() { condcount++; signal(mutex); wait(condsem); condcount--; } signal() { if (condcount > 0) signal(condsem); else signal(mutex); }
Implementazioni del tipo monitor OB:
struct Monitor { int mutex; // altre variabili e metodi del monitor };```html
//semaforo che garantisce la mutua esclusione
int num_var_cond; //numero di variabili condition
int id_conds; //id del gruppo di semafori associati alle var. cond.
int *cond_counts; //array delle variabili condition_count
int id_shared; //identificativo memoria condivisa
/*crea quindi un array di int (cond_counts)che indicano quanti processi ci sono in attesa su ognuna variabile condition ed inoltre crea
*un array di semafori (id_conds) associato alle variabili condition sul quale verranno effettuate le wait}; init_monitorvoid (Monitor*, int);
enter_monitor(Monitor*);
void leave_monitor(Monitor*);
void remove_monitor(Monitor*);
void wait_condition(Monitor*,int);
void signal_condition(Monitor*,int);
voidSpecifica del tipo monitor OO:
Monitor {
class
private:
int mutex;
int num_var_cond;
int id_conds;
int *cond_counts;
int id_shared;
public:
Monitor(int);
virtual ~Monitor();
wait_condition(int);
void signal_condition(int);
void enter();
void leave();
};
Implementazione OB:
init_monitorvoid (Monitor *M,int
```num_var){
M->num_var_cond=num_var;
M->mutex=semget(IPC_PRIVATE,1,IPC_CREAT|0664); //crea un array di 1 semaforo
M->id_conds=semget(IPC_PRIVATE,num_var,IPC_CREAT|0664); //crea un array di num_var semafori
semctl(M->mutex,0,SETVAL,1); //setta il valore del mutex a 0
for (int i=0;i<num_var;i++) //setta tutto l'array di num_var semafori a 0
semctl(M->id_conds,i,SETVAL,0);
M->id_shared=shmget(IPC_PRIVATE,num_var*sizeof(int),IPC_CREAT|0664); //alloca un contatore per ogni var.condition
M->cond_counts=(int*) (shmat(M->id_shared,0,0)); //effettua l'attach all'array di contatori appena allocato
for (int i=0;i<num_var;i++)
M->cond_counts[i]=0; // setta tutte le variabili condition a zero
}
/* In pratica è stata creata una memoria condivisa alla quale è collegato tramite il shmat(...) l'array di variabili condition cond_counts con un
* puntatore a int. Tutti gli elementi di questo array sono stati poi inizializzati a zero tramite il ciclo */
for.*/} wait_condition(Monitor*void M,int id_var) //id_var indica l'elemento dell'array cond_counts sul quale effettuare la wait { M->cond_counts[id_var]=M->cond_counts[id_var]+1; // aggiunge 1 all'elemento cond_counts[id_var],ciò indica che c'è un processo in attesa sulla var. cond. Numero id_var Signal_Sem(M->mutex,0); //effettua una signal sul mutex Wait_Sem(M->id_conds,id_var); // effettua una wait sul semaforo dell'array id_conds di numero id_var. M->cond_counts[id_var]=M->cond_counts[id_var]-1; // così diminuisce di 1 il conto dei processi sospesi sulla var. id_var } signal_condition(Monitor*void M,int id_var) { if(M->cond_counts[id_var]>0) // Se ci sono processi in attesa sulla var. cond. Indicata ne sblocca uno. { Signal_Sem(M->id_conds,id_var); } else //Altrimenti effettua una signal sul mutex { Signal_Sem(M->mutex,0); } }
<!-- Sblocca semplicemente il monitor -->
enter_monitor(Monitor* M){
Wait_Sem(M->mutex,0);
}
leave_monitor(Monitor* M){
Signal_Sem(M->mutex,0);
}
remove_monitor(Monitor* M){
semctl(M->mutex,0,IPC_RMID); // Rimuove il mutex principale
semctl(M->id_conds,M->num_var_cond,IPC_RMID); // Rimuove l'array di semafori associato alle var. cond.
shmctl(M->id_shared,IPC_RMID,0); // Cancella la memoria condivisa. IMPORTANTE!!!
}
<!-- Scambio Di Messaggi -->
Un messaggio è un blocco di informazioni, senza alcun formato predefinito, UNIX supporta il modello nel quale lo scambio di messaggi avviene tra un utente e una mailbox (comunicazione indiretta)
Una mailbox può essere vista come una coda di messaggi. È caratterizzata da:
- Una chiave (analoga a quella dei segmenti di memoria condivisa);
- Un proprietario (l'utente che la istanzia);
- Un gruppo di appartenenza;
- Un insieme di protezioni (indicate dalla solita stringa con 3 numeri a 3 bit)
Per istanziare una coda di messaggi, utilizziamo la funzione msgget(key, flag)
. Questa funzione restituisce l'ID della risorsa oppure -1 se si verifica un errore. Il parametro flag
può assumere il valore IPC_CREAT | 0664
se vogliamo creare una nuova coda di messaggi, oppure 0 se vogliamo utilizzare una coda già istanziata.
Esempio:
int open_queue(key_t keyval) {
int qid;
if ((qid = msgget(keyval, IPC_CREAT | 0660)) == -1)
return -1;
return qid;
}
Per inviare un messaggio alla mailbox, utilizziamo la funzione msgsnd(msqid, msgp, msgsz, msgflg)
. Questa funzione invia un messaggio sulla coda msqid
. Il messaggio è contenuto all'interno del buffer msgbuf
. La struttura del buffer è la seguente:
struct msgbuf {
long message_type;
char message_text[MAX_SIZE];
}
Il campo message_type
identifica il tipo del messaggio. Questo campo diventa molto importante quando utilizziamo funzioni di ricezione selettive, perché ci permette di estrarre un particolare messaggio dalla coda anche se non si trova in cima.
Se il campo message_type
assume il valore del PID del mittente, possiamo realizzare una...
- Il campo è una stringa. Può essere sostituito con qualsiasi altro tipo di messaggio (intero, double, etc..)
- Il campo rappresenta la dimensione (in byte) del messaggio da inviare
- Il campo è un flag che ci permette di specificare la semantica dell'operazione di send
- Se = 0 la send blocca il processo se la mailbox è piena
- Se = IPC_NOWAIT la send ritorna -1 e non accoda il messaggio se la mailbox è piena
Esempio:
int result;
int msqid;
struct message {
long type;
char text[20];
} msg;
msg.type = 1;
strcpy(msg.text, "This is message 1");
result = msgsnd(msqid, (void *) &msg, sizeof(msg.text), IPC_NOWAIT);
Per ricevere un messaggio dalla mailbox:
int msgrcv(int msqid, void *msgp, int msgsz, long msgtyp, int msgflg);
- msqid: Consuma un messaggio dalla mailbox identificata da
- msgp: Il buffer contenente il messaggio sarà puntato da
msgsz• Il campo messaggio di tale buffer avrà dimensionemsgtyp• Il campo (tipo del messaggio) svolge importanti funzioni:msgtyp• Se = 0 viene prelevato il primo messaggio della coda (ovvero quello inviato da più tempo);msgtyp msgtyp• Se > 0 viene prelevato il primo messaggio dalla coda il cui campo tipo sia pari al valore dimsgtyp |msgtyp|• Se < 0 viene prelevato il primo messaggio dalla coda il cui campo tipo abbia una valore minore o uguale amsgflg• Il campo se impostato a 0 indica una ricezione bloccante: se non ci sono messaggi da consumare nella mailbox, il processomsgrcvsi sospende sulla fino al giungere del messaggio. Se impostato a IPC_NOWAIT, la ricezione non é bloccante: se non ci sonomessaggi viene restituito -1
Esempio:
int result;
int msqid;
struct message {
long type;
char text[20];
} msg;
long msgtyp = 0;
result = msgrcv(msqid, (void *) &msg, sizeof(msg.text),msgtyp, IPC_NOWAIT);
Primitiva di controllo
int msgctl(int msqid,
int cmd, struct msqid_ds *buf);
cmd
: L'operazione da eseguire dipende dal valore di cmd
che può valere:
- IPC_STAT
per la lettura della coda senza consumo
- IPC_SET
per modificare le caratteristiche della coda
- IPC_RMID
per la deallocazione
Posix Threads:
Per la creazione di un Thread utilizzare la funzione pthread_create
:
pthread_create(thread, attr, start_r, arg)
- thread
(output): di tipo pthread_t
, è un identificatore del thread creato
- attr
(input): di tipo pthread_attr_t
, serve a settare gli attributi del thread
- start_r
(input): puntatore (di tipo void*
) alla funzione C (starting routine) che verrà eseguita una volta che il thread è creato
- Firma di una starting routine di esempio: void* foo(void*)
- arg
(input): argomento (di tipo void*
) che può essere passato alla funzione C (ne va fatto il casting a void*
)
Per il passaggio di parametri nella creazione di un thread si usa la variabile arg
che consente di passare un valore di tipo void*
.singolo argomento di tipo void *; Per passare più di un argomento al thread, si può definire e passare una struct, facendone il casting a void *:
```html
struct dati {
int dato1;
char dato2;
};
dati *d;
d->dato1 = 10;
d->dato2 = 'c';
pthread_create(&id, NULL, start_f, (void *) d)
```
Nel thread sarà sufficiente effettuare il casting inverso:
```html
dati* miei_dati = (struct dati *) d;
```
Per la terminazione di un Thread:
- La starting routine termina la sua esecuzione: `pthread_exit(status);` (status)
- Il thread chiama la funzione `pthread_cancel();` dove è lo stato di uscita del Thread
- L'intero processo termina.
Per la creazione e distruzione di Mutex:
```html
pthread_mutex_init(mutex, attr)
```
Crea un nuovo mutex e lo inizializza come "sbloccato" (unlocked).
- mutex (output): di tipo pthread_mutex_t è un identificatore del mutex creato
- attr (input): per settare gli attributi del mutex.
<mutex>
<pthread_mutex_destroy> (mutex)
Distrugge un mutex che non serve più.
</pthread_mutex_destroy>
<pthread_mutex_lock> (mutex)
Un thread invoca la lock su un mutex per acquisire l'accesso in mutua esclusione alla sezione critica relativa al mutex.
</pthread_mutex_lock>
</mutex>