Home > Mobile >  How to initialize an array of semaphore and use it?
How to initialize an array of semaphore and use it?

Time:03-17

I would like to create an array of 5 semaphore such that only the first semaphore value has a value of 1 {1,0,0,0,0}. However, when I run the code, it gives me segfault. Am i initializing it correctly and using it correctly? Basically since the first semaphore has a value of 1 the first child will not be blocked, after the first child executing successfully, it will signal the second child to execute , etc.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <semaphore.h>

#define NUM_PROCESSES 5

int main()
{

    int i, j, pid;
    int shmid;
    sem_t *sem[5];

    shmid = shmget(IPC_PRIVATE, sizeof(sem_t) * 5 , IPC_CREAT | 0600);
    *sem = (sem_t *)shmat(shmid, NULL, 0);

    sem_init(sem[0], 1, 1);

    for (int k = 1; k < NUM_PROCESSES; k  )
    {
        sem_init(sem[k], 1, 0);
    }

    for (i = 0; i < NUM_PROCESSES; i  )
    {
        if ((pid = fork()) == 0)
        {
            break;
        }
    }

    if (pid == 0)
    {
        sem_wait(sem[i]);
        printf("I am child %d\n", i);

        for (j = i * 10; j < i * 10   10; j  )
        {
            printf("%d ", j);
            fflush(stdout);
            usleep(250000);
        }

        printf("\n\n");
        sem_post(sem[i   1]);
        shm_destroy(sem[i]);
    }
    else
    {
        for (i = 0; i < NUM_PROCESSES; i  )
        {
            wait(NULL);
        }
        shmctl(shmid, IPC_RMID, 0);
    }
}


Second edit: Using sem without pointer, now my issue becomes after printing the first child, the program hangs (non terminate and not printing anything).

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <semaphore.h>

#define NUM_PROCESSES 5

int main()
{

    int i, j, pid;
    int shmid;
    sem_t sem[5];

    sem_init(&sem[0], 1, 1);

    for (int k = 1; k < NUM_PROCESSES; k  )
    {
        sem_init(&sem[k], 1, 0);
    }

    for (i = 0; i < NUM_PROCESSES; i  )
    {
        if ((pid = fork()) == 0)
        {
            break;
        }
    }

    if (pid == 0)
    {
        sem_wait(&sem[i]);

        printf("I am child %d\n", i);

        for (j = i * 10; j < i * 10   10; j  )
        {
            printf("%d ", j);
            fflush(stdout);
            usleep(250000);
        }

        printf("\n\n");

        if (i   1 < NUM_PROCESSES)
        {
            sem_post(&sem[i   1]);
        }
    }
    else
    {
        for (i = 0; i < NUM_PROCESSES; i  )
        {
            wait(NULL);
        }
    }
}

Third version: Using a shared memory region but run into segfault again. I do not understand what is going on now.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <semaphore.h>

#define NUM_PROCESSES 5

int main()
{

    int i, j, pid;
    int shmid;
    sem_t *sem[NUM_PROCESSES];

    shmid = shmget(IPC_PRIVATE, sizeof(sem_t) * NUM_PROCESSES, IPC_CREAT | 0600);
    *sem = (sem_t *)shmat(shmid, NULL, 0);

    sem_init(sem[0], 1, 1);

    for (int k = 1; k < NUM_PROCESSES; k  )
    {
        sem_init(sem[k], 1, 0);
    }

    for (i = 0; i < NUM_PROCESSES; i  )
    {
        if ((pid = fork()) == 0)
        {
            break;
        }
    }

    if (pid == 0)
    {
        if (sem_post(sem[i   1]) != -1)
        {
            printf("hello");
        }
        sem_wait(sem[i]);

        printf("I am child %d\n", i);

        for (j = i * 10; j < i * 10   10; j  )
        {
            printf("%d ", j);

            fflush(stdout);
            usleep(250000);
        }

        printf("\n\n");

        if (i   1 < NUM_PROCESSES)
        {
            sem_post(sem[i   1]);
        }
    }
    else
    {
        for (i = 0; i < NUM_PROCESSES; i  )
        {
            wait(NULL);
        }
    }
}

CodePudding user response:

An array of sem_t on the stack (as in OP's second attempt) is not suitable for inter-process communication because the current state of the semaphores would be copied to the child processes by fork() and the child processes would operate on their own copies of the semaphores. So the array of sem_t should be initialized within a shared memory segment (as in OP's first attempt). In the calls to fork(), the child process inherits the attached shared memory segment of the parent, so all the child processes will use the same semaphores in that memory segment.

The code in the OP's first attempt is incorrect. It is allocating the correct amount of memory, but is treating the memory as an array of sem_t * instead of an array of sem_t. It is assigning a pointer to the shared memory segment to the first element of the array, but is using other elements of the array without initializing them.

Those number 5s ought to be replaced with the macro NUM_PROCESSES for consistency. Also, there is no need to explicitly cast the return value of the shmat call to sem_t * because conversion of void * to another object type does not need an explicit cast.

Old code:

    sem_t *sem[5];

    shmid = shmget(IPC_PRIVATE, sizeof(sem_t) * 5 , IPC_CREAT | 0600);
    *sem = (sem_t *)shmat(shmid, NULL, 0);

    sem_init(sem[0], 1, 1);

    for (int k = 1; k < NUM_PROCESSES; k  )
    {
        sem_init(sem[k], 1, 0);
    }

Corrected code:

    sem_t *sem;

    shmid = shmget(IPC_PRIVATE, sizeof(sem_t) * NUM_PROCESSES, IPC_CREAT | 0600);
    sem = shmat(shmid, NULL, 0);

    sem_init(&sem[0], 1, 1);

    for (int k = 1; k < NUM_PROCESSES; k  )
    {
        sem_init(&sem[k], 1, 0);
    }

The original code in the child that calls sem_post on the next semaphore needs to check that there is a next semaphore:

Old code:

        sem_post(sem[i   1]);

Corrected code:

        if (i   1 < NUM_PROCESSES)
        {
            sem_post(&sem[i   1]);
        }

The original code in the child has a call shm_destroy(sem[i]);. That is not a standard POSIX function. I guess OP meant to call sem_destroy(sem[i]);. With the previous corrections, that should now be sem_destroy(&sem[i]);.

Old code:

        shm_destroy(sem[i]);

Corrected code:

        sem_destroy(&sem[i]);

Putting it all together:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <semaphore.h>

#define NUM_PROCESSES 5

int main(void)
{

    int i, j;
    pid_t pid;
    int shmid;
    sem_t *sem;

    shmid = shmget(IPC_PRIVATE, sizeof(sem_t) * NUM_PROCESSES, IPC_CREAT | 0600);
    sem = shmat(shmid, NULL, 0);

    sem_init(&sem[0], 1, 1);

    for (int k = 1; k < NUM_PROCESSES; k  )
    {
        sem_init(&sem[k], 1, 0);
    }

    for (i = 0; i < NUM_PROCESSES; i  )
    {
        if ((pid = fork()) == 0)
        {
            break;
        }
    }

    if (pid == 0)
    {
        sem_wait(&sem[i]);
        printf("I am child %d\n", i);

        for (j = i * 10; j < i * 10   10; j  )
        {
            printf("%d ", j);
            fflush(stdout);
            usleep(250000);
        }

        printf("\n\n");
        if (i   1 < NUM_PROCESSES)
        {
            sem_post(&sem[i   1]);
        }
        sem_destroy(&sem[i]);
    }
    else
    {
        for (i = 0; i < NUM_PROCESSES; i  )
        {
            wait(NULL);
        }
        shmctl(shmid, IPC_RMID, 0);
    }
}
  • Related