1
INTER-PROCESS
COMMUNICATION (IPC) SYSTEM
(Bach: “the Design of the
Unix Operating System”; manuale on-line)
- Memoria Condivisa (Shared Memory) - Semafori
- Code di Messaggi
(Sulle sito del corso trovate alcuni programmi C che illustrano l’uso di queste system call)
2
IPC
• Le Inter-Process Communication Utilities (di solito abbreviato in IPC) sono una serie di system call che permettono di allocare ed usare determinate strutture per la comunicazione e la sincronizzazione tra processi in Unix
• Sebbene comunicazione e sincronizzazione possano essere realizzate in Unix anche senza l’uso delle IPC, queste ultime offrono dei meccanismi molto più sofisticati e versatili
3
Memoria Condivisa
P1
--- ---
P2
--- ---
C I A O read
read
write read
write write
• Quattro syscall sono disponibili per gestire la memoria condivisa (MC): shmget, shmctl, shmat, shmdt
4
Memoria Condivisa
• Normalmente, ogni processo possiede uno spazio di indirizzamento logico separato da tutti gli altri
• Un segmento di MC invece, può essere letto e/o scritto da due o più processi, e permette quindi un rapido scambio di informazioni.
• Quindi, ogni processo usa il segmento di MC come se fosse una normale porzione del proprio spazio di indirizzamento logico, che però è in comune a più processi
• Tipicamente, l’accesso ai dati presenti in un segmento di MC deve essere regolato come una sezione critica
5
Memoria Condivisa
• Quando più processi devono usare un segmento di MC, uno di questi preleva il segmento (shmget), e rende noto agli altri processi l’identificatore con cui potrà essere usato il segmento.
• un segmento appena prelevato è un’ area di RAM “grezza”.
Per poter essere usato da un processo, deve essere
“attaccato” (shmat) allo spazio di indirizzamento di quel processo, specificando anche il tipo della struttura dati che verrà memorizzata nel segmento (uguale per tutti i processi)
• Quando un processo non vuole più usare un segmento, lo
“stacca” (shmdt) dal suo spazio di indirizzamento, o lo rimuove (shmctl).
6
/* ESEMPIO DI PRELIEVO E USO DELLA MEMORIA
CONDIVISA: shmuse.c */
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
main() {
int i,n,w;
int shmid;
char buffer[10]; /* l’array che metteremo nella shared memory */
char *pun1, *pun2;
shmid = shmget(IPC_PRIVATE, sizeof(buffer[10])*10, 0666);
if (shmid == -1) {
printf("errore creazione memoria\n");
exit(0);
}
preleva un nuovo segmento di memoria
dimensione del segmento
protezioni sul segmento
7
else printf("prelevata memoria id = %d\n",shmid);
n = fork(); /* facciamo conto che non possa fallire... */
if ( n == 0) { /* processo figlio */
printf("(figlio) mando un messaggio al padre
su memoria condivisa\n");
pun1 = (char *)shmat(shmid, 0, SHM_RND);
pun1[0] = 'c';
pun1[1] = 'i'; pun1[2] = 'a';
pun1[3] = 'o'; pun1[4] = '\0';
printf("(figlio) ora aspetto 3 secondi\n");
for (i=0; i<3; i++) { system("sleep 1");
printf(".\n");
}
printf("(figlio) ho finito e muoio\n");
exit(10);
}
attacca il segmento dove possibile nello spazio
dati del processo read/write (flag is not
SHM_RDONLY)
8
else /* processo padre */
{
printf("(padre) aspetto il figlio e leggo il suo messaggio\n");
w = wait(0);
pun2 = (char *)shmat(shmid,0,SHM_RND);
printf("(padre) messaggio del figlio: ");
for (i = 0; i < 5; i++)
printf("%c",pun2[i]);
printf("(padre) ora rimuovo la memoria condivisa e muoio anch'io\n");
shmctl(shmid,IPC_RMID,0);
exit(0);
}
} rimuovi il
segmento
9
output del programma shmuse.c
prelevata memoria id = 100
(figlio) mando un messaggio al padre su mem. cond.
(figlio) ora aspetto 3 secondi
(padre) aspetto il figlio e leggo il suo messaggio .
. .
(figlio) ho finito e muoio
(fpadre) messaggio dal figlio: ciao
(padre) rimuovo la mem. cond. e muoio anch’io
10
Semafori
P1
--- ---
P2
--- ---
semaforo
wait signal
• Tre syscall sono disponibili per gestire i semafori:
semget, semctl, semop
11
Semafori
• La syscall semget permette di prelevare un array di semafori (da 1 a MAXINT semafori)
• Per gestire i semafori, si deve usare un array di elementi di tipo sembuf (tipo predefinito) che conterranno informazioni relative al corrispondente semaforo
semaforo 0 semaforo 1 semaforo 2 ...
sem_num sem_op sem_flg
posizione della struttura (e del corrispondente semaforo) nell’array valore da sommare alla variabile semaforica
che fare se il semaforo è ≤ 0 sembuf:
12
/* ESEMPIO DI PRELIEVO E USO DI UN SET DI SEMAFORI:
semuse.c */
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
main() {
int i,n,w,semval,semid;
struct sembuf sops[1];
semid = semget(IPC_PRIVATE,1,0666);
if (semid == -1){
printf("errore creazione set di semafori\n");
exit(0);
} else
printf("\nprelevato set di semafori id = %d\n",semid);
array di strutture sembuf per gestire i semafori prelevati
preleva un nuovo set di semafori quanti semafori nel set
read/write per tutti
13
i = semctl(semid,0,GETVAL,0);
printf("\nvalore iniziale del primo semaforo = %d\n",i);
n = fork();
if (n == -1){
fprintf(stderr,"fork fallita\n");
exit(1);
}
else if ( n == 0) /* processo figlio */{
sops[0].sem_num = 0; /* operazione sul semaforo 0 */
sops[0].sem_op = -1; /* decrementa il semaforo di 1: WAIT */
sops[0].sem_flg = 0; /* che fare: wait se semaforo = 0 */
printf("\n(figlio) tento di decrementare il semaforo, mi sospendo se vale 0\n");
i = semop(semid,sops,1);
printf("\n(figlio) ho decrementato il semaforo e muoio\n");
exit(0);
}
notate la ridondanza...
lunghezza dell’array sops
14
else /* processo padre */
{
printf("\n(padre) ora aspetto 2 secondi\n");
for (i=0; i<2; i++) {
system("sleep 1");
printf("*\n");
}
printf("\n(padre)ora sveglio il figlio incrementando il
semaforo\n");
sops[0].sem_num = 0; /* operazione sul semaforo 0 */
sops[0].sem_op = 1; /* tipo di operazione: incrementa il semaforo: SIGNAL */
sops[0].sem_flg = 0; /* che fare: ininfluente */
i = semop(semid,sops,1);
15
printf("\n(padre) ora aspetto altri 2 secondi\n");
for (i=0; i<2; i++) {
system("sleep 1");
printf("*\n");
}
printf("\n(padre) ora rimuovo il semaforo e muoio anch'io\n");
semctl(semid,IPC_RMID,0);
exit(0);
} }
16
output del programma semuse.c
prelevata set di semafori id = 100 valore del semaforo = 0
(figlio) tento di decrementare il sem., mi sospendo se vale 0 (padre) ora aspetto 2 secondi
*
*
(padre) ora sveglio il figlio incrementando il semaforo (figlio) ho decrementato il semaforo e muoio
(padre) ora aspetto due secondi
*
*
(padre) ora rimuovo il semaforo e muoio anch’io
17
Code di Messaggi
P1 --- ---
m1 m2 m2 ...
send
send receive
• Quattro syscall sono disponibili per gestire le code di messaggi: msgget, msgsnd, msgrcv, msgctl
P3 --- ---
P2 --- ---
P4 --- ---
receive
receive
send
18
Code di Messaggi
• Quando più processi devono usare una coda di messaggi, uno di questi preleva la coda (msgget), e rende noto agli altri processi l’identificatore della coda
• più processi possono inviare (msgsnd) e ricevere (msgrcv) messaggi sulla stessa coda. Un messaggio ricevuto da un processo viene “consumato”.
• Ogni messaggio ha un tipo (indicato da un numero usato nella msgsnd). Un processo in ascolto su una coda può decidere di ricevere qualsiasi messaggio, o solo quelli di un determinato tipo. Se nessun messaggio di quel tipo è
presente nella coda, il processo può decidere di sospendersi.
19
/* USO DI UNA CODA DI MESSAGGI: msguse.c */
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
main( ){
int i,n,w;
int msgid;
struct msgbuf1 { /* struttura che contiene: */
long mtype; /* tipo del messaggio; */
char mtext[100] /* il messaggio; */
} sndbuf, rcvbuf, *msgp;
msgid = msgget(IPC_PRIVATE,0666);
if (msgid == -1) {
printf("errore creazione coda di messaggi\n");
exit(0);
} else printf("\nprelevata coda di messaggi id = %d\n",msgid);
20
n = fork();
if (n == -1){
fprintf(stderr,"fork fallita\n");
exit(1);
}
else if ( n == 0){ /* processo figlio */
printf("\n(figlio) ora aspetto 3 secondi\n");
for (i=0; i<3; i++) {
system("sleep 1"); printf(".\n");
}
printf("\n(figlio) invio un messaggio al padre e muoio\n");
msgp = &sndbuf;
msgp->mtype = 5;
strcpy(msgp->mtext,"BUONGIORNO");
msgsnd(msgid,msgp,sizeof("BUONGIORNO"),0);
exit(0);
}
puntatore alla struttura che contiene il messaggio
dimensione del messaggio
ininfluente
21
else /* processo padre */
{
msgp = &rcvbuf;
printf("\n(padre) leggo il messaggio del figlio");
printf("\nmi sospendo se non e' ancora arrivato\n");
i = msgrcv(msgid,msgp,100,5,0);
printf("\n(padre) messaggio ricevuto: %s\n",msgp->mtext);
printf("\n(padre) ora rimuovo la coda di messaggi e muoio\n");
msgctl(msgid,IPC_RMID,0);
exit(0);
} }
puntatore alla struttura che ospiterà il messaggio letto
massima lunghezza di messaggio leggibile
ricevo solo messaggi di tipo 5
se non ci sono messaggi del tipo specificato mi metto in attesa
22
output del programma msguse.c
prelevata coda di messaggi id = 100 (figlio) ora aspetto 3 secondi
(padre) leggo il messaggio del figlio,
mi sospendo se non è ancora arrivato .
.
(figlio) invio un messaggio al padre e muoio (padre) messaggio ricevuto: BUONGIORNO (padre) ora rimuovo la coda di messaggi e muoio
23
Visualizzare e rimuovere le risorse
$> ipcs
T ID KEY MODE OWNER
Message Queues:
q 10 0x003e8 --rw-r--r-- st103010
q 20 0x003e8 --rw-rw-r-- st102210
Shared Memory:
m 25 0x003e8 --rw-rw-rw- st103010
Semaphores:
s 280 0x003e8 --rw-rw-rw- st245020
$> ipcrm –q 10 –s 280 –m 25
• Le risorse non vengono deallocate quando il processo che le ha prelevate termina. Occorre farlo esplicitamente (con il comando o la system call opportuna)