如何初始化信号量数组并使用它?

How to initialize an array of semaphore and use it?

我想创建一个包含 5 个信号量的数组,这样只有第一个信号量值的值为 1 {1,0,0,0,0}。但是,当我 运行 代码时,它给了我段错误。我是否正确初始化并正确使用了它?基本上,由于第一个信号量的值为 1,因此第一个 child 不会被阻塞,在第一个 child 成功执行后,它将向第二个 child 发出信号以执行,等等

#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);
    }
}


第二次编辑:使用不带指针的 sem,现在我的问题是在打印第一个 child 之后,程序挂起(非终止且不打印任何内容)。

#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);
        }
    }
}

第三个版本:使用共享内存区域但 运行 再次陷入段错误。我不明白现在是怎么回事。

#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);
        }
    }
}

堆栈上的 sem_t 数组(如 OP 的第二次尝试)不适合 inter-process 通信,因为信号量的当前状态将被 fork() 并且子进程将对它们自己的信号量副本进行操作。因此 sem_t 的数组应该在共享内存段内初始化(如 OP 的第一次尝试)。在对 fork() 的调用中,子进程继承了父进程附加的共享内存段,因此所有子进程将在该内存段中使用相同的信号量。

OP 第一次尝试中的代码不正确。它正在分配正确数量的内存,但将内存视为数组 sem_t * 而不是 sem_t 的数组。它正在将指向共享内存段的指针分配给数组的第一个元素,但正在使用数组的其他元素而不对其进行初始化。

那些数字 5 应该用宏 NUM_PROCESSES 替换以保持一致性。此外,不需要将 shmat 调用的 return 值显式转换为 sem_t *,因为将 void * 转换为另一种对象类型不需要显式转换。

旧代码:

    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);
    }

更正后的代码:

    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);
    }

在下一个信号量上调用 sem_post 的子项中的原始代码需要检查 下一个信号量:

旧代码:

        sem_post(sem[i + 1]);

更正后的代码:

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

原码中子有调用shm_destroy(sem[i]);。这不是标准的 POSIX 函数。我猜 OP 的意思是调用 sem_destroy(sem[i]);。通过之前的更正,现在应该是 sem_destroy(&sem[i]);.

旧代码:

        shm_destroy(sem[i]);

更正后的代码:

        sem_destroy(&sem[i]);

综合起来:

#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);
    }
}