Makefile
make -f altronome
Compilazione=>comando: make oppure per compilare con un file “altronome”.
target: dipendenze [tab] comando di sistema
- Per compilazione separata:
file.o : file.cpp file.h => se il file dipende da file.h e file.cpp
gcc -c file.cpp
- Per unire i vari moduli
eseguibile: obj1.o … objN.o
gcc -o eseguibile obj1.o … objN.o
make clean
- Chiudere sempre il makefile con un clean da invocare prima della compilazione con un
clean: rm -f *.o rm -f *~ rm -f *kdev*
Esempio
start: lib.o main.o
g++ -o start lib.o main.o
lib.o: lib.cpp lib.h
g++ -c lib.cpp
main.o: main.cpp lib.h
g++ -c main.cpp
Shared memory
Tutte le risorse IPC (interprocess comunication) tra cui Shared memory (SHM), semafori (SEM) e code di messaggi (MSG) sono identificate da una chiave univoca IPC KEY che può essere generata tramite
key_t ftok(char * path, char id)
Restituisce una chiave combinando degli elementi con il carattere indicato come secondo argomento.
Get e ctl
Le 2 primitive comuni sono get e ctl: get restituisce il descrittore della risorsa e ctl (control) permette di effettuare delle operazioni sulla risorsa.
int …get (key_t key, …, int flag);
Dove key è una chiave generata con ftok() e flag indica il metodo di creazione e i permessi, ad esempio:
IPC_CREAT|0664
int …ctl (int desc, …, int cmd, …);
Dove desc è il descrittore della risorsa e cmd indica il comando da eseguire.
Comandi di controllo da shell
ipcs <-m|-s|-q>
Visualizza tutte le risorse allocate.
ipcrm <shm|sem|msg> <IPC_ID>
Rimuove una data struttura noto il suo identificatore.
Utilizzo SHM
Creazione della SHM=>Collegamento alla SHM=>Uso della SHM=>Scollegamento della SHM=>Eliminazione della SHM
Creazione
int shmget(key_t key, int size, int flag)
size: dimensione della memoria.
flag: intero che specifica la modalità di creazione e dei permessi di accesso (IPC_CREAT, IPC_EXCL, permessi).
Esempio:
key_t chiave = 40;
int ds_shm;
ds_shm = shmget(chiave, 1024,IPC_CREAT | IPC_EXCL |0664);
Collegamento
void* shmat(int shmid, const void *shmaddr,int flag)
Collega il segmento di memoria allo spazio di indirizzamento del chiamante.
shmid : identificatore del segmento di memoria
shmaddr : indirizzo dell’area di memoria del processo chiamante al quale collegare il segmento di memoria condivisa. Se è un puntatore nullo il segmento di memoria è il primo disponibile e viene selezionato dal sistema.
Controllo
int shmctl(int ds_shm, int cmd, struct shmid_ds * buff)
Invoca l’esecuzione di un comando su una SHM.
ds_shm : descrittore della memoria condivisa su cui si vuole operare
cmd: specifica del comando da eseguire:
IPC_STAT, IPC_SET
IPC_RMID: da eliminare, rimuove solo quando non vi sono più processi attaccati
SHM_LOCK: impedisce che il segmento venga swappato o paginato
buff : puntatore ad una struttura di tipo shmid_ds con eventuali parametri per i comandi IPC_STAT e IPC_SET
Sys call
Ogni processo ha un unico identificatore di processo (Process Identifier: PID), intero tra 0 e 30000.
Un processo ottiene il suo pid attraverso la chiamata di sistema:
int getpid();
Un processo ottiene il pid del padre attraverso la chiamata di sistema:
int getppid();
Sospende (transizione stato Blocked) un processo per un certo numero di secondi:
sleep(int sec);
La creazione di nuovi processi è gestita mediante la chiamata di sistema:
int fork(void);
- Il figlio eredita gli stessi valori delle variabili, i descrittori agli stessi file aperti, e anche il Program Counter!
- Le modifiche apportate alle proprie variabili da uno dei due processi non sono visibili all’altro.
Per comunicare al padre lo stato di terminazione dei figli:
int wait(int *stato);
Un processo termina con la chiamata di sistema exit:
exit(0);
L’unico modo in cui un programma può essere eseguito da Unix è che il processo esistente invii una chiamata di sistema exec. Esistono varie versioni della exec:
int execlp (char *nomefile, char *arg0, .., char *argn,(char *) 0);
int execl (char *pathname, char *arg0, .., char *argn,(char *) 0);
Una system call che crea un processo senza copiare l’immagine dal padre al figlio:
vfork();
Semafori
Creazione ed inizializzazione di un semaforo
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, …);
key_t chiave_sem=IPC_PRIVATE;
Esempio:
//richiesta di 2 semafori ed inizializzazione
sem=semget(chiave_sem,2,IPC_CREAT|0664);
//Inizializzazione dei due semafori
semctl(sem,0,SETVAL,val1); semctl(sem,1,SETVAL,val2);
Operazioni sui semafori, wait e signal
wait(semaphorevoid s){ s.value--;if (s.value<0){ s.queue.insert(Process);suspend(Process);}}
signal(semaphorevoid s){ s.value++;if (s.value<=0){ s.queue.remove(Process);wake-up(Process);}}
Semaphore operations:
int semop(int semid, struct sembuf *sops, unsigned nsops);
Ognuno degli nsops elementi, riferiti dal puntatore sops, specifica l’operazione da compiere sul semaforo. L’operazione è descritta da una struttura, struct sembuf.
- sem_op < 0 : wait_for_zero
- sem_op==0 : signal
- sem_op > 0 :
Rimozione di una struttura semaforica:
semctl(id_sem, num_sem ,IPC_RMID);
La variabile num_sem in questo caso viene ignorata.
Semafori implementazione
Ricorda che la struct sembuf contiene i seguenti campi:
unsigned short sem_num; // numero di semaforo
short sem_op; // operazione da compiere
short sem_flg; // flags
static void Wait_Sem(int,int);
static void Signal_Sem(int,int);
static int Queue_Sem(int,int); //restituisce il num di processi in attesa su un semaforo
Wait_Sem(intvoid id_sem, int numsem){ struct sembuf sem_buf;sem_buf.sem_num=numsem;sem_buf.sem_flg=0;sem_buf.sem_op=-1;semop(id_sem,&sem_buf,1); //semaforo rosso}
Queue_Sem(intint id_sem, int numsem){ return (semctl(id_sem,numsem,GETNCNT,NULL)); }
Signal_Semvoid (int id_sem,int numsem){ struct sembuf sem_buf;sem_buf.sem_num=numsem;sem_buf.sem_flg=0;sem_buf.sem_op=1;semop(id_sem,&sem_buf,1); //semaforo verde}
Monitor
Permette la gestione di processi in base a delle condizioni. La sospensione di un processo nel caso in cui la condizione non sia verificata, avviene utilizzando un nuovo tipo di variabile, detta variabile di tipo condition:
var_cond x;
Sulla quale possono essere compiute signal e wait.
x.wait; x.signal;
Provoca la sospensione del processo fino a che un altro processo esegue.
Ovviamente se non vi è alcun processo in attesa sulla variabile x, la signal non ha alcun effetto.
Signal and wait
Sia P un processo sospeso su una variabile condition x e Q il processo che esegue la signal su tale variabile.
Signal_and_wait prevede che il processo P risvegliato riprenda immediatamente l'esecuzione e che Q venga sospeso;
Signal and urgent wait
Prevede che il processo Q abbia la priorità su ogni altro processo che intende entrare nel monitor. Ciò si può ottenere sospendendo il processo Q su un'apposita coda (urgent_queue).
Signal and continue
Detto anche wait and notify privilegia il processo segnalante rispetto al segnalato. Il processo Q segnalante prosegue la sua esecuzione, mantenendo l'