Почему мой мьютекс не работает должным образом в многопроцессорном приложении C? - PullRequest
5 голосов
/ 27 ноября 2010

Я взламываю однозначное задание и столкнулся с проблемой в моем коде, которая должна порождать 2 процесса, когда второй процесс ждет завершения 1-го перед выполнением.Вот что у меня есть:

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

Теперь я знаю, что в «реальном мире» это действительно глупо.Если мой «потребитель» ничего не делает, пока мой «продюсер» не закончил, то у вас также может не быть двух процессов, подобных этому, но задание пытается проиллюстрировать состояние гонки, поэтому, почему нам конкретно сказалисделайте это таким образом.

Итак, моя проблема в том, что потребительский процесс не ждет производителя.Я предположил, что, поскольку семафор был снят в производителе (sem_wait(&mutex);), он не будет доступен потребителю, пока не будет вызван sem_post(&mutex); в производителе.

Кроме того, как можно лучшескажем, строка wait(NULL); не ожидает завершения обоих процессов.

Я что-то критически неправильно понял?

Ответы [ 4 ]

10 голосов
/ 27 ноября 2010

У вас должна быть проверка ошибок в ваших вызовах семафоров. Используйте 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) ожидает только одного дочернего процесса для выхода. Вызовите его дважды, чтобы дождаться двух дочерних процессов.

4 голосов
/ 27 ноября 2010

Только потому, что вы fork поток производителя первый, не означает, что ОС запланирует его запуск первым - вполне возможно, что потребитель фактически запускается и получает блокировку первым.

Кроме того, вы должныпроверьте возвращаемое значение sem_wait - его можно вернуть без удержания семафора.

Также вполне возможно (как отметили несколько человек в комментариях), что семафоры могут просто не работать через fork ed процессы

EDIT - если вы передадите ненулевое значение аргументу 2 из sem_init(sem_t *sem, int pshared, unsigned value) при инициализации семафоров posix будет работать в процессах

EDIT - см. здесь для гораздо лучшего объяснения, чем я мог бы дать, в комплекте с исходным кодом, чтобы сделать почти точно то, что вы хотите

1 голос
/ 27 ноября 2010

Вы предоставили полный код в вопросе?

Если это так, вам не хватает инициализации семафора.Вы должны позвонить либо sem_init, либо sem_open перед использованием семафора.

Чтение здесь .

РЕДАКТИРОВАНИЕ Вы указываете pshared = 0 в вызове sem_init.Это делает семафор процесс локальным (то есть его можно использовать только для синхронизации потоков одного процесса).fork создает дочерний процесс, поэтому семафор не делает ничего полезного.

Если pshared имеет значение 0, то семафор разделяется между потоками процесса.Если pshared не равен нулю, то семафор распределяется между процессами.

(цитата из ссылки выше)

0 голосов
/ 27 ноября 2010

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

Во-вторых, вам, возможно, придется инициализировать мьютексы, иначе их использование может быть неопределенным.

...