为什么我的互斥锁在多进程C应用程序中不能正常工作?

时间:2010-11-27 13:16:05

标签: c multithreading unix semaphore

我正在攻击单一的任务,并且遇到了我的代码的问题,该代码应该产生2个进程,其中第二个进程在执行之前等待1st完成。这就是我到目前为止所做的:

sem_t mutex;
int producer; int consumer;
sem_init(&mutex, 0, 1);
producer = fork();
consumer = fork();

if (producer == 0) {
    if (VERBOSE) printf("Running producer\n");
    /* down semaphore */
    sem_wait(&mutex);
    /* START CRITICAL REGION */
    get_files(N);
    /* END CRITICAL REGION */
    /* up semaphore */
    sem_post(&mutex);
    if (VERBOSE) printf("Ending producer\n");
    exit(0);
}

if (consumer == 0) {
    if (VERBOSE) printf("Running consumer\n");
    /* down semaphore */
    sem_wait(&mutex);
    /* START CRITICAL REGION */
    /* do stuff */
    /* END CRITICAL REGION */
    /* up semaphore */
    sem_post(&mutex);
    if (VERBOSE) printf("Ending consumer\n");
    exit(0);
}
/* parent waits for both to complete */
wait(NULL);

现在,我知道在“现实世界”中,这真的很愚蠢。如果我的'消费者'在我的'制作人'完成之前什么都不做,那么你可能也没有这样的2个过程,但是这个任务试图说明一个竞争条件,所以为什么我们被特别告知这样做。

所以,我的问题是消费者流程不是在等待生产者。我假设由于信号量在生产者(sem_wait(&mutex);)中被删除,因此在生产者中调用sem_post(&mutex);之前,消费者无法使用它。

此外,据我所知,行wait(NULL);并未等待两个流程完成。

我是否严重误解了某些事情?

4 个答案:

答案 0 :(得分:10)

您应该对信号量调用进行错误检查。如果perror()sem_wait()sem_init()返回非零,请使用sem_post()来显示错误。

其次,你创造了比你想象的更多的流程。您的第一个fork()会导致父级(producer非零)和子级(producer为零)。 两个进程然后执行第二个fork(),因此您现在有四个进程。

第三,必须在进程之间共享sem_t变量,因此必须将其存储在共享内存区域中。实现此目标的最简单方法是mmap()

sem_t *sem = mmap(NULL, sizeof *sem, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

(在sem_init()和第一个fork()之前执行。

第四,它没有定义哪个进程会先运行,所以你不能依赖于在消费者之前调用sem_wait()的生产者线程。相反,使用sem_init()将信号量初始化为零,并在消费者中仅 调用sem_wait() - 这将阻止消费者。生产者执行,并在完成后调用sem_post(),这允许消费者继续。

sem_init()调用应将pshared指定为非零,将value指定为0,因此它应如下所示:

if (sem_init(sem, 1, 0) != 0) {
    perror("sem_init");
    exit(1);
}

第五,wait(NULL)仅等待单个子进程退出。调用它两次等待两个子进程。

答案 1 :(得分:4)

仅仅因为你fork生产者线程首先并不意味着操作系统会安排它先运行 - 消费者很可能实际运行并首先获得锁定。

另外,你应该检查sem_wait的返回值 - 它可以在不保持信号量的情况下从它返回。

很可能(正如有几个人在评论中指出的那样)信号量可能无法在fork个过程中起作用

编辑 - 如果在初始化posix信号量 跨进程工作时将{0}传递给sem_init(sem_t *sem, int pshared, unsigned value)的参数2

编辑 - 请参阅here以获得比我能给出的更好的解释,完整的源代码几乎完全符合您的要求

答案 2 :(得分:1)

您是否在问题中提供了完整的代码?

如果是这样,您就缺少信号量初始化。在使用信号量之前,您必须先调用sem_initsem_open

阅读here

编辑您在pshared来电中指定sem_init = 0。这使信号量进程本地化(即它只能用于同步一个进程的线程)。 fork创建了一个子进程,因此信号量没有做任何有用的事情。

  

如果pshared的值为0,那么   信号量是在。之间共享的   一个过程的线程。如果是pshared   非零,然后共享信号量   进程之间。

(引用来自上面的链接)

答案 3 :(得分:0)

首先,我认为互斥体不会与进程一起使用。原因是分叉进程实际上并不共享内存。他们将能够读取与fork之前相同的内存,但只要其中一个内存写入内存,新进程就会收到自己的副本。

其次,您可能必须初始化互斥锁,否则可能未定义它们的使用。