simple semaforos hilos ejercicios ejemplo c linux fork shared-memory semaphore

semaforos - ejercicios de hilos en c



¿Cómo usar los semáforos POSIX en procesos bifurcados en C? (2)

El problema al que te enfrentas es la mala interpretación de la función sem_init() . Cuando leas la página del manual verás esto:

El argumento pshared indica si este semáforo se compartirá entre los hilos de un proceso o entre procesos.

Si ha terminado de leer hasta este punto, pensará que el valor distinto de cero de pshared hará que el semáforo entre semáforos entre procesos. Sin embargo, esto está mal. Debe continuar leyendo y comprenderá que debe ubicar el semáforo en una región de memoria compartida. Para hacer eso, se pueden usar varias funciones como se puede ver a continuación:

Si pshared es distinto de cero, entonces el semáforo se comparte entre procesos y debe ubicarse en una región de memoria compartida (consulte shm_open (3), mmap (2) y shmget (2)). (Dado que un hijo creado por fork (2) hereda las asignaciones de memoria de sus padres, también puede acceder al semáforo). Cualquier proceso que pueda acceder a la región de memoria compartida puede operar en el semáforo utilizando sem_post (3), sem_wait (3), etc. .

Considero que este enfoque es más complicado que otros, por lo que quiero animar a las personas a usar sem_open() lugar de sem_init() .

A continuación puede ver un programa completo que ilustra lo siguiente:

  • Cómo asignar memoria compartida y usar variables compartidas entre procesos bifurcados.
  • Cómo inicializar un semáforo en una región de memoria compartida y es utilizado por múltiples procesos.
  • Cómo bifurcar múltiples procesos y hacer que el padre espere hasta que todos sus hijos salgan.

#include <stdio.h> /* printf() */ #include <stdlib.h> /* exit(), malloc(), free() */ #include <sys/types.h> /* key_t, sem_t, pid_t */ #include <sys/shm.h> /* shmat(), IPC_RMID */ #include <errno.h> /* errno, ECHILD */ #include <semaphore.h> /* sem_open(), sem_destroy(), sem_wait().. */ #include <fcntl.h> /* O_CREAT, O_EXEC */ int main (int argc, char **argv){ int i; /* loop variables */ key_t shmkey; /* shared memory key */ int shmid; /* shared memory id */ sem_t *sem; /* synch semaphore *//*shared */ pid_t pid; /* fork pid */ int *p; /* shared variable *//*shared */ unsigned int n; /* fork count */ unsigned int value; /* semaphore value */ /* initialize a shared variable in shared memory */ shmkey = ftok ("/dev/null", 5); /* valid directory name and a number */ printf ("shmkey for p = %d/n", shmkey); shmid = shmget (shmkey, sizeof (int), 0644 | IPC_CREAT); if (shmid < 0){ /* shared memory error check */ perror ("shmget/n"); exit (1); } p = (int *) shmat (shmid, NULL, 0); /* attach p to shared memory */ *p = 0; printf ("p=%d is allocated in shared memory./n/n", *p); /********************************************************/ printf ("How many children do you want to fork?/n"); printf ("Fork count: "); scanf ("%u", &n); printf ("What do you want the semaphore value to be?/n"); printf ("Semaphore value: "); scanf ("%u", &value); /* initialize semaphores for shared processes */ sem = sem_open ("pSem", O_CREAT | O_EXCL, 0644, value); /* name of semaphore is "pSem", semaphore is reached using this name */ printf ("semaphores initialized./n/n"); /* fork child processes */ for (i = 0; i < n; i++){ pid = fork (); if (pid < 0) { /* check for error */ sem_unlink ("pSem"); sem_close(sem); /* unlink prevents the semaphore existing forever */ /* if a crash occurs during the execution */ printf ("Fork error./n");  } else if (pid == 0) break; /* child processes */ } /******************************************************/ /****************** PARENT PROCESS ****************/ /******************************************************/ if (pid != 0){ /* wait for all children to exit */ while (pid = waitpid (-1, NULL, 0)){ if (errno == ECHILD) break; } printf ("/nParent: All children have exited./n"); /* shared memory detach */ shmdt (p); shmctl (shmid, IPC_RMID, 0); /* cleanup semaphores */ sem_unlink ("pSem"); sem_close(sem); /* unlink prevents the semaphore existing forever */ /* if a crash occurs during the execution */ exit (0); } /******************************************************/ /****************** CHILD PROCESS *****************/ /******************************************************/ else{ sem_wait (sem); /* P operation */ printf (" Child(%d) is in critical section./n", i); sleep (1); *p += i % 3; /* increment *p by 0, 1 or 2 based on i */ printf (" Child(%d) new value of *p=%d./n", i, *p); sem_post (sem); /* V operation */ exit (0); } }

SALIDA

./a.out shmkey for p = 84214791 p=0 is allocated in shared memory. How many children do you want to fork? Fork count: 6 What do you want the semaphore value to be? Semaphore value: 2 semaphores initialized. Child(0) is in critical section. Child(1) is in critical section. Child(0) new value of *p=0. Child(1) new value of *p=1. Child(2) is in critical section. Child(3) is in critical section. Child(2) new value of *p=3. Child(3) new value of *p=3. Child(4) is in critical section. Child(5) is in critical section. Child(4) new value of *p=4. Child(5) new value of *p=6. Parent: All children have exited.

No está mal comprobar shmkey ya que cuando ftok() falla, devuelve -1. Sin embargo, si tiene varias variables compartidas y si la función ftok() falla varias veces, las variables compartidas que tienen una shmkey con valor -1 residirán en la misma región de la memoria compartida, lo que resultará en un cambio en una que afectará a la otra. Por lo tanto la ejecución del programa se ensuciará. Para evitar esto, es mejor verificar si ftok() devuelve -1 o no (es mejor verificar el código fuente en lugar de imprimir en la pantalla como lo hice yo, aunque quería mostrarle los valores clave en caso de que haya una colisión) .

Preste atención a cómo se declara e inicializa el semáforo. Es diferente de lo que has hecho en la pregunta ( sem_t sem vs sem_t* sem ). Además, debes usarlos como aparecen en este ejemplo. No puede definir sem_t* y usarlo en sem_init() .

Quiero bifurcar múltiples procesos y luego usar un semáforo en ellos. Esto es lo que intenté:

sem_init(&sem, 1, 1); /* semaphore*, pshared, value */ . . . if(pid != 0){ /* parent process */ wait(NULL); /* wait all child processes */ printf("/nParent: All children have exited./n"); . . /* cleanup semaphores */ sem_destroy(&sem); exit(0); } else{ /* child process */ sem_wait(&sem); /* P operation */ printf(" Child(%d) is in critical section./n",i); sleep(1); *p += i%3; /* increment *p by 0, 1 or 2 based on i */ printf(" Child(%d) new value of *p=%d./n",i,*p); sem_post(&sem); /* V operation */ exit(0); }

Y la salida es:

child(0) forked child(1) forked Child(0) is in critical section. Child(1) is in critical section. child(2) forked Child(2) is in critical section. child(3) forked Child(3) is in critical section. child(4) forked Child(4) is in critical section. Child(0) new value of *p=0. Child(1) new value of *p=1. Child(2) new value of *p=3. Child(3) new value of *p=3. Child(4) new value of *p=4. Parent: All children have exited.

Esto claramente significa que el semáforo no funcionó como se suponía. ¿Puedes explicar cómo debo usar los semáforos en los procesos bifurcados?


Linux mínimo anónimo sem_init + mmap MAP_ANONYMOUS ejemplo

Me gusta esta configuración, ya que no contamina ningún espacio de nombres global como sem_open .

El único inconveniente es que MAP_ANONYMOUS no es POSIX, y no conozco ningún reemplazo: ¿Memoria compartida anónima? shm_open por ejemplo, toma un identificador global al igual que sem_open .

C Principal:

#define _GNU_SOURCE #include <assert.h> #include <semaphore.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main(int argc, char **argv) { pid_t pid; typedef struct { sem_t sem; int i; } Semint; Semint *semint; size_t size = sizeof(Semint); semint = (Semint *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); assert(semint != MAP_FAILED); /* 1: shared across processes * 0: initial value, wait locked until one post happens (making it > 0) */ sem_init(&semint->sem, 1, 0); semint->i = 0; pid = fork(); assert(pid != -1); if (pid == 0) { sleep(1); semint->i = 1; msync(&semint->sem, size, MS_SYNC); sem_post(&semint->sem); exit(EXIT_SUCCESS); } if (argc == 1) { sem_wait(&semint->sem); } /* Was modified on the other process. */ assert(semint->i == 1); wait(NULL); sem_destroy(&semint->sem); assert(munmap(semint, size) != -1); return EXIT_SUCCESS; }

Compilar:

gcc -g -std=c99 -Wall -Wextra -o main main.c -lpthread

Ejecutar con sem_wait :

./main

Ejecutar sin sem_wait :

./main 1

Sin esta sincronización, es probable que la afirmación falle, ya que el niño duerme durante un segundo entero:

main: main.c:39: main: Assertion `semint->i == 1'' failed.

Probado en Ubuntu 18.04. GitHub aguas arriba .