Hai presente il problema del produttore e del consumatore?

Fai conto di avere una coda del genere, in cui un produttore inserisce e un consumatore estrae

in pseudo codice
codice:
#include <stdio.h> 
#include <stdlib.h> 

typedef struct _MT_BUFFER
{
	semaphore_t push_sem;
	semaphore_t pop_sem;
	mutex_t exclusive;

	void ** buffer;
	int head;
	int tail;
	unsigned int bufsize;
}*MT_BUFFER;

MT_BUFFER MT_BUFFER_new(unsigned int size)
{
	MT_BUFFER temp = shm_alloc(sizeof(struct _MT_BUFFER));
	temp->buffer = (void**)shm_alloc(sizeof(void*)size);
    //controllare allocazione non sarebbe male
	sem_init(temp->push_sem, size);
	sem_init(temp->pop_sem, 0);
	mutex_init(temp->exclusive, UNLOCKED);
	bufsize = size;
	head=size-1;
	tail=size-1;
	return temp;
}

void MT_BUFFER_free(MT_BUFFER queue)
{
	sem_terminate(queue->push_sem);
	sem_terminate(queue->pop_sem);
	mutex_terminate(queue->exclusive);
	shm_free(queue->buffer);
	shm_free(queue);
}

void mt_push(MT_BUFFER queue, void* data)
{
	sem_down(queue->push_sem);
	mutex_lock(queue->exclusive);
	queue->buffer[tail] = data;
	--queue->tail;
	if(queue->tail<0) 
		queue->tail = queue->bufsize - 1;
	
	mutex_unlock(queue->exclusive);
	sem_up(queue->push_sem);
}

void* mt_pop(MT_BUFFER queue)
{
	void * retval;
	sem_down(queue->pop_sem);
	mutex_lock(queue->exclusive);
	retval = queue->buffer[queue->head];
	head = (head+1) % queue->bufsize;	
	mutex_unlock(queue->exclusive);
	sem_up(queue->push_sem);
	return retval;
}
a questo punto la prima parte del problema, (i clienti che arrivano al bancone) dovranno inserire le loro richieste in un buffer di questo genere, il buffer dovrà avere almeno n posti, tanti quanti sono i camerieri.

codice:
//var. globale in memoria condivisa
MT_BUFFER bancone = MT_BUFFER_new(n_camerieri);
ogni cliente che arriva inserisce il suo ordine nel bancone

codice:
    struct ordinazione ord;
    ord.primo = PASTA_E_FAGIOLI;
    ord.secondo = MELANZANE_ALLA_PARMIGIANA;
    ord.cliente = get_processID();
    mt_push(bancone, &ord);
dopo di che aspetta di essere chiamato dal cassiere
codice:
   msg = wait_for_message();
tra il cameriere e il cuoco ci sarà un'altra coda uguale identica
ma siccome sicuramente i cuochi saranno molto + lenti dei camerieri
questa coda sarà molto + lunga del numero di cuochi, altrimenti i camerieri saranno spesso bloccati in attesa dei cuochi

codice:
//var. globale in memoria condivisa
MT_BUFFER cucina = MT_BUFFER_new(n_cuochi * 10);
il loop del cameriere sarà una cosa di questo tipo
codice:
struct ordinazione * ord;
while (running)
{
    ord = (struct ordinazione *)mt_pop(bancone);
    mt_push(cucina, ord);
}
ora il principio si ripete anche tra cuochi e cassieri, il buffer lo lasciamo un po più grande per fare in modo che il cuoco non si debba sincronizzare troppo spesso con il cassiere.

codice:
//var. globale in memoria condivisa
MT_BUFFER casse = MT_BUFFER_new(n_cassieri * 2);
il loop del cuoco
codice:
struct ordinazione * ord;
struct preparazione *prep;//anche questo punta alla mem condivisa
while(running)
{
    ord = (struct ordinazione *)mt_pop(cucina);
    prep = preparazione_new();
    prep.primo = cucinailprimo(ord.primo);
    prep.secondo = cucinailsecondo(ord.secondo);
    prep.cliente = ord.cliente;
    mt_push(casse, prep);
}
invece il cassiere si comporta diversamente, legge dalla coda delle casse e urla a gran voce il numero del cliente che si sta servendo, il cliente si presenta, paga e se ne va...

ecco il loop del cassiere, qua conviene usare qualche primitiva di scambio di messaggi per evitare di impantanarsi in decine di chiamate per la sincronizzazione
codice:
struct preparazione *prep; //
int soldi, resto;
message_t msg;
while(running)
{
    prep = (struct preparazione *)mt_pop(casse);
    prep.conto = costo(prep.primo) + costo(prep.secondo);
    prep.cassiere = get_processID();
    send_msg(prep.cliente, prep);
    msg = wait_for_message();
    soldi = (int)msg.data;
    metti_in_cassa(soldi);
    resto =  soldi - conto;
    send_msg(prep.cliente, resto);
}
il cliente l'avevamo lasciato in attesa

codice:
     msg = wait_for_message();
     prep = (struct preparazione*)msg.data;
     send_message(prep.cassiere, caccia_i_soldi(prep.conto));
     msg = wait_for_message();
     resto = (int)msg.data;
     mangia(prep.primo);
     mangia(prep.secondo);
     shm_free(prep);
     exit(0);
Questa è un idea, come implementarlo poi in C te lo lascio, perchè è un lavoro lungo..., nel main metterai tutti i fork necessari, accetterai l'imput e gestirai i segnali..

COmunque, riassumendo, l'uso della coda sincronizzata ti permette di scrivere un programma fair (se fosse uno stack forse qualche cliente potrebbe morire di fame), ti permette di evitare di sbatterti troppo a pensare come sincronizzare i vari personaggi: perchè ci pensa la coda, e usa relativamente poche risorse (2 semafori e un mutex, in tutto il programma sono 6 semafori e 3 mutex).