'The pid works only once after the two calls to scanf

This program creates a pid and shares two integers (base and height) through the shared memory.

The parent process time asks four times to insert two integers and wait for the pid to calculate the area.

After the four cycles, the ppid waits for the end of its child, deletes the shared memory and the semaphore and ends. The child waits for the new variable (base, height with semaphore), calculates the area, and prints it, and after the four iterations, it ends.

I have to use semaphore for process synchronization and to regulate the critical section. The problem consists of the pid, it works only one time.

I'm expected to receive the area after i put the integer, the first time work but the following time doesent.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <string.h>

#define SEM_KEY (key_t)8765
#define SHM_KEY (key_t)9876

/** Questa union la trovate digitando "man semctl" */
union semun
{
    int val;               /* Value for SETVAL */
    struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
    unsigned short *array; /* Array for GETALL, SETALL */
    struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */
};

/** Tipo di dato condiviso */
struct sh_data
{
    int altezza;
    int base;
};

/** Funzioni di appoggio per operare con i semafori */
int sem_set(int semid, int val);
int sem_down(int semid);
int sem_up(int semid);

void controlla_area(int, struct sh_data *);

int main()
{
    pid_t pid;
    int semid, shmid, i;
    struct sh_data *data;

    // SEMAFORO
    // Creazione del semaforo
    semid = semget(SEM_KEY, 1, 0666 | IPC_CREAT);

    // Inizializzazione semaforo
    sem_set(semid, 1);

    // SHARED MEMORY
    // Creazione shared memory
    shmid = shmget(SHM_KEY, sizeof(struct sh_data), 0666 | IPC_CREAT);

    // Attach della shm allo spazio di indirizzi del processo
    data = (struct sh_data *)shmat(shmid, NULL, 0);

    // Processo figlio
    pid = fork();
    switch (pid)
    {
    case -1:
        perror("Errore fork");
        exit(EXIT_FAILURE);
    case 0:
        controlla_area(semid, data);
        exit(EXIT_SUCCESS);
    default:
        break;
    }

    // Processo padre
    for (i = 0; i < 4; i++)
    {
        sem_down(semid);
        printf("Inserisci la base: ");
        scanf("%d", &(data->base));
        printf("Inserisci l'altezza: ");
        scanf("%d", &(data->altezza));
        sem_up(semid);
        wait(NULL);
    }

    // Detach della shared memory
    shmdt(data);

    // Eliminazione della shared memory
    shmctl(shmid, IPC_RMID, NULL);

    // Eliminazione semaforo
    semctl(semid, 0, IPC_RMID);

    exit(EXIT_SUCCESS);
}

void controlla_area(int semid, struct sh_data *data)
{
    int area;

    sem_down(semid);
    area = data->base * data->altezza;
    printf("L'area del triangolo è %d\n", area);
    sem_up(semid);
}

int sem_set(int semid, int val)
{
    union semun s;
    s.val = val;
    /* Inizializza il valore del semaforo */
    return semctl(semid, 0, SETVAL, s);
}

int sem_down(int semid)
{
    struct sembuf buff;
    buff.sem_num = 0;
    buff.sem_op = -1;
    buff.sem_flg = SEM_UNDO;
    /* Decrementa il valore del semaforo */
    return semop(semid, &buff, 1);
}

int sem_up(int semid)
{
    struct sembuf buff;
    buff.sem_num = 0;
    buff.sem_op = 1;
    buff.sem_flg = SEM_UNDO;
    /* Incrementa il valore del semaforo */
    return semop(semid, &buff, 1);
}


Solution 1:[1]

The problem consists of the pid, it works only one time.

This is because the child process calls controlla_area just once, and executes its contents

void controlla_area(int semid, struct sh_data *data)
{
    int area;

    sem_down(semid);
    area = data->base * data->altezza;
    printf("L'area del triangolo è %d\n", area);
    sem_up(semid);
}

just once, and then exits.

Note that both the parent process and the child process will vie for control of the shared memory via the semaphore. There is no guarantee as to which process will first decrement the semaphore, but it is likely the parent process will, due to the overhead of starting a new process (see also: context switch).

The fact that the child process does surely gain access to the semaphore is made possible by the wait(NULL); that blocks the execution of the parent process, during the first iteration of the loop that writes to shared memory, until the child process terminates.

Placing a blocking action in the parent process, after the switch statement (e.g., sleep(5)) will make the race condition obvious. The child process will likely get some CPU time, and will gain access to the shared memory via the semaphore decrement. It will then invoke Undefined Behaviour by reading the indeterminate values in the shared memory.

Incrementing a semaphore is not a blocking action. There is no guarantee that the process that just incremented the semaphore will not be the same process to next decrement the semaphore. This is a matter of scheduling, and is implementation defined.

A single semaphore should not be relied upon to create this kind of synchronization between processes.


With minimal changes, an additional semaphore can create a lockstep between the parent process and the child process, such that each process waits for the other to complete its work.

A loop is added to the child process.

The semaphores are initialized such that the parent process operates first. The additional sem_down(semid_to); ensures the parent process waits for the child process to finish printing, before terminating it via a signal.

#define _POSIX_C_SOURCE 200809L
#include <signal.h>

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <string.h>


#define SEM_KEY (key_t)8765
#define SHM_KEY (key_t)9876

/** Questa union la trovate digitando "man semctl" */
union semun
{
    int val;               /* Value for SETVAL */
    struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
    unsigned short *array; /* Array for GETALL, SETALL */
    struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */
};

/** Tipo di dato condiviso */
struct sh_data
{
    int altezza;
    int base;
};

/** Funzioni di appoggio per operare con i semafori */
int sem_set(int semid, int val);
int sem_down(int semid);
int sem_up(int semid);

void controlla_area(int, int, struct sh_data *);

int main()
{
    pid_t pid;
    int semid_to, semid_from, shmid, i;
    struct sh_data *data;

    // SEMAFORO
    // Creazione del semaforo
    semid_to = semget(SEM_KEY, 1, 0666 | IPC_CREAT);
    semid_from = semget(1 + SEM_KEY, 1, 0666 | IPC_CREAT);

    // Inizializzazione semaforo
    sem_set(semid_to, 1);
    sem_set(semid_from, 0);

    // SHARED MEMORY
    // Creazione shared memory
    shmid = shmget(SHM_KEY, sizeof(struct sh_data), 0666 | IPC_CREAT);

    // Attach della shm allo spazio di indirizzi del processo
    data = (struct sh_data *)shmat(shmid, NULL, 0);

    // Processo figlio
    pid = fork();
    switch (pid)
    {
    case -1:
        perror("Errore fork");
        exit(EXIT_FAILURE);
    case 0:
        controlla_area(semid_to, semid_from, data);
        exit(EXIT_SUCCESS);
    default:
        break;
    }

    // Processo padre
    for (i = 0; i < 4; i++)
    {
        sem_down(semid_to);
        printf("Inserisci la base: ");
        scanf("%d", &(data->base));
        printf("Inserisci l'altezza: ");
        scanf("%d", &(data->altezza));
        sem_up(semid_from);
    }

    sem_down(semid_to);
    kill(pid, SIGTERM);
    wait(NULL);

    // Detach della shared memory
    shmdt(data);

    // Eliminazione della shared memory
    shmctl(shmid, IPC_RMID, NULL);

    // Eliminazione semaforo
    semctl(semid_to, 0, IPC_RMID);
    semctl(semid_from, 0, IPC_RMID);

    exit(EXIT_SUCCESS);
}

void controlla_area(int semid_to, int semid_from, struct sh_data *data)
{
    int area;

    while (1) {
        sem_down(semid_from);
        area = data->base * data->altezza;
        printf("L'area del triangolo è %d\n", area);
        sem_up(semid_to);
    }
}

int sem_set(int semid, int val)
{
    union semun s;
    s.val = val;
    /* Inizializza il valore del semaforo */
    return semctl(semid, 0, SETVAL, s);
}

int sem_down(int semid)
{
    struct sembuf buff;
    buff.sem_num = 0;
    buff.sem_op = -1;
    buff.sem_flg = SEM_UNDO;
    /* Decrementa il valore del semaforo */
    return semop(semid, &buff, 1);
}

int sem_up(int semid)
{
    struct sembuf buff;
    buff.sem_num = 0;
    buff.sem_op = 1;
    buff.sem_flg = SEM_UNDO;
    /* Incrementa il valore del semaforo */
    return semop(semid, &buff, 1);
}

Note that there are many things that could go wrong here, due to the fact that almost no error handling is done whatsoever. semget, semctl, semop, shmget, shmat, shmctl, shmdt, scanf, kill and wait can all fail.

In a non-trivial program none of these return values should be ignored with regards to errors.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1