2015-12-12 2 views
1

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

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

PARENT 
PARENT 
CHILD 
PARENT 
CHILD 
PARENT 

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

Для компиляции, введите: 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

Пройдите через свой код в голове. Как родительский, так и дочерний сервер печатают сразу без какой-либо синхронизации. –

+1

Потому что вы вызываете 'display' перед операцией семафора. – fvu

ответ

2

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

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

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

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

2-й аргумент должен быть 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.

+0

ОК спасибо большое, он разбирал много вещей в моей голове. –