C Language
Interprocess Communication (IPC)
Ricerca…
introduzione
I meccanismi di comunicazione tra processi (IPC) consentono a diversi processi indipendenti di comunicare tra loro. Lo standard C non fornisce alcun meccanismo IPC. Pertanto, tutti questi meccanismi sono definiti dal sistema operativo host. POSIX definisce un ampio insieme di meccanismi IPC; Windows definisce un altro set; e altri sistemi definiscono le proprie varianti.
semafori
I semafori vengono utilizzati per sincronizzare le operazioni tra due o più processi. POSIX definisce due diversi set di funzioni semaforo:
- 'System V IPC' -
semctl()
,semop()
,semget()
. - 'POSIX Semaphores' -
sem_close()
,sem_destroy()
,sem_getvalue()
,sem_init()
,sem_open()
,sem_post()
,sem_trywait()
,sem_unlink()
.
Questa sezione descrive i semafori IPC System V, così chiamati perché originati da Unix System V.
Innanzitutto, devi includere le intestazioni richieste. Le vecchie versioni di POSIX richiedevano #include <sys/types.h>
; POSIX moderno e la maggior parte dei sistemi non lo richiedono.
#include <sys/sem.h>
Quindi, dovrai definire una chiave sia nel genitore che nel bambino.
#define KEY 0x1111
Questa chiave deve essere la stessa in entrambi i programmi o non si riferiscono alla stessa struttura IPC. Ci sono modi per generare una chiave concordata senza codificare il suo valore.
Successivamente, a seconda del compilatore, potrebbe essere necessario o meno eseguire questo passaggio: dichiarare un'unione ai fini delle operazioni del semaforo.
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
Quindi, definisci le strutture try ( semwait
) e raise ( semsignal
). I nomi P e V provengono dall'olandese
struct sembuf p = { 0, -1, SEM_UNDO}; # semwait
struct sembuf v = { 0, +1, SEM_UNDO}; # semsignal
Ora, inizia prendendo l'id per il tuo semaforo IPC.
int id;
// 2nd argument is number of semaphores
// 3rd argument is the mode (IPC_CREAT creates the semaphore set if needed)
if ((id = semget(KEY, 1, 0666 | IPC_CREAT) < 0) {
/* error handling code */
}
Nel genitore, inizializza il semaforo per avere un contatore di 1.
union semun u;
u.val = 1;
if (semctl(id, 0, SETVAL, u) < 0) { // SETVAL is a macro to specify that you're setting the value of the semaphore to that specified by the union u
/* error handling code */
}
Ora puoi decrementare o incrementare il semaforo di cui hai bisogno. All'inizio della tua sezione critica, decrementi il contatore usando la funzione semop()
:
if (semop(id, &p, 1) < 0) {
/* error handling code */
}
Per incrementare il semaforo, usi &v
invece di &p
:
if (semop(id, &v, 1) < 0) {
/* error handling code */
}
Nota che ogni funzione restituisce 0
in caso di successo e -1
in caso di fallimento. Non controllare questi stati di ritorno può causare problemi devastanti.
Esempio 1.1: Corse con discussioni
Il programma seguente avrà un fork
processo e sia padre che figlio tenteranno di stampare caratteri sul terminale senza alcuna sincronizzazione.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
}
}
}
Uscita (1a corsa):
aAABaBCbCbDDcEEcddeFFGGHHeffgghh
(2a corsa):
aabbccAABddBCeeCffgDDghEEhFFGGHH
Compilare ed eseguire questo programma dovrebbe darti un output diverso ogni volta.
Esempio 1.2: Evita le corse con i semafori
Modificando l' Esempio 1.1 per usare i semafori, abbiamo:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KEY 0x1111
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};
struct sembuf p = { 0, -1, SEM_UNDO};
struct sembuf v = { 0, +1, SEM_UNDO};
int main()
{
int id = semget(KEY, 1, 0666 | IPC_CREAT);
if(id < 0)
{
perror("semget"); exit(11);
}
union semun u;
u.val = 1;
if(semctl(id, 0, SETVAL, u) < 0)
{
perror("semctl"); exit(12);
}
int pid;
pid = fork();
srand(pid);
if(pid < 0)
{
perror("fork"); exit(1);
}
else if(pid)
{
char *s = "abcdefgh";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(13);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(14);
}
sleep(rand() % 2);
}
}
else
{
char *s = "ABCDEFGH";
int l = strlen(s);
for(int i = 0; i < l; ++i)
{
if(semop(id, &p, 1) < 0)
{
perror("semop p"); exit(15);
}
putchar(s[i]);
fflush(stdout);
sleep(rand() % 2);
putchar(s[i]);
fflush(stdout);
if(semop(id, &v, 1) < 0)
{
perror("semop p"); exit(16);
}
sleep(rand() % 2);
}
}
}
Produzione:
aabbAABBCCccddeeDDffEEFFGGHHgghh
Compilare ed eseguire questo programma ti darà la stessa uscita ogni volta.