Синхронизация процессов в C не выполняется в первый раз

Так что у меня возникла эта проблема с программой синхронизации процессов в C.

Я должен сделать код, который, используя fork(), будет производить что-то вроде этого:

PARENT
PARENT
CHILD
PARENT
CHILD
PARENT

Используя код, который я нашел здесь, я смог заставить его работать, но по некоторым причинам первый результат, который появляется на экране, искажен, в то время как все остальные работают просто отлично.

Для компиляции введите: gcc test.c display.c -o test -pthread

В любом случае, вот код, который я тестирую (повторяю: это не мой код):

#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

int main(void)
{
  int i;
  /* place semaphore in shared memory */
  sem_t *sema = mmap(NULL, sizeof(sema), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);
  /* create/initialize semaphore */
  if ( sem_init(sema, 1, 0) < 0)
  {
    perror("sem_init");
    exit(EXIT_FAILURE);
  }
  int nloop=10;
  int pid = fork();
  if (pid == 0)
  { 
    for (i = 0; i < nloop; i++)
    {
      // child unlocks semaphore
      display("CHILD\n");
      if (sem_post(sema) < 0)
          perror("sem_post");
      sleep(1);
    }
    if (munmap(sema, sizeof(sema)) < 0)
    {
      perror("munmap");
      exit(EXIT_FAILURE);
    }
      exit(EXIT_SUCCESS);
  }
  else
  {
    for (i = 0; i < nloop; i++)
    { // parent starts waiting
      display("PARENT\n");
      if (sem_wait(sema) < 0) 
        perror("sem_wait");
    // parent finished waiting
    }
    if (sem_destroy(sema) < 0)
    {
      perror("sem_destroy failed");
      exit(EXIT_FAILURE);
    }
    if (munmap(sema, sizeof(sema)) < 0)
    {
      perror("munmap failed");
      exit(EXIT_FAILURE);
    }
    exit(EXIT_SUCCESS);
  }
}

Вот вывод:

PACREHNT
ILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD
PARENT
CHILD

Почему это происходит в начале?

1 ответ

Решение

Вы не можете написать программу, которая чередует родительский и дочерний процессы с одним семафором (не прибегая к какой-либо форме занятого ожидания с флагами или чем-то еще), потому что оба процесса будут стремиться получить семафор; нет никакого способа предсказать, какой процесс получит его первым. Ваш код (кроме первой итерации), кажется, работает должным образом, потому что ребенок спит очень долго, но технически это все еще состояние гонки, нет никакой гарантии, что родитель получит шанс приобрести семафор до того, как ребенок просыпается (хотя это очень маловероятно).

Итак, вам нужно 2 семафора: один используется ребенком, чтобы уведомить родителя, что его очередь, и другой используется родителем, чтобы уведомить ребенка. Чтобы выбрать, кто начинает первым, инициализируйте соответствующий семафор 1 (а другой - 0).

Кроме того, это неправильно:

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

Второй аргумент должен быть sizeof(*sema) поскольку вы хотите выделить память для объекта семафора, а не для указателя.

Ты никогда #include "display.h"Вы, вероятно, должны.

Обработка ошибок может быть улучшена, но для этой игрушечной программы я не думаю, что это имеет большое значение.

Вот рабочая версия, использующая подход с 2 семафорами (при этом родительский семафор инициализируется равным 1, поэтому родительский элемент запускается первым):

#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

int main(void)
{
    int i;
    /* place semaphore in shared memory */
    sem_t *child_sem = mmap(NULL, sizeof(*child_sem), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);
    sem_t *parent_sem = mmap(NULL, sizeof(*parent_sem), PROT_READ |PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1, 0);

    /* create/initialize semaphore */
    if ( sem_init(child_sem, 1, 0) < 0)
    {
        perror("sem_init");
        exit(EXIT_FAILURE);
    }

    if (sem_init(parent_sem, 1, 1) < 0) {
        perror("sem_init");
        exit(EXIT_FAILURE);
    }

    int nloop=10;
    int pid = fork();
    if (pid == 0)
    {
        for (i = 0; i < nloop; i++)
        {
            if (sem_wait(child_sem) < 0)
                perror("sem_wait");
            display("CHILD\n");
            if (sem_post(parent_sem) < 0)
                perror("sem_post");
            sleep(1);
        }
    }
    else
    {
        for (i = 0; i < nloop; i++)
        { // parent starts waiting
            if (sem_wait(parent_sem) < 0) 
                perror("sem_wait");
            display("PARENT\n");
            if (sem_post(child_sem) < 0)
                perror("sem_post");
        }
    }
}

Я удалил munmap(2) а также sem_destroy(3) призывает к краткости, и потому что они не нужны, так как процесс в любом случае завершается.

Обратите внимание, что оба процесса следуют одному и тому же шаблону: дождитесь своего семафора, сделайте работу, оповестите семафор другого процесса. Это хорошая возможность провести некоторый рефакторинг и переместить все это в функцию, которая получает отображаемую строку, семафор для ожидания и семафор для последующего уведомления.

Вы также должны привыкнуть к компиляции с -Wall,

Другие вопросы по тегам