2013-06-24 1 views
3
#include <semaphore.h> 

int main(void) 
{ 
    int pfds[2]; 
    char buff[20]; 
    sem_t sem; 

    sem_init(&sem, 1, 1); 
    pipe(pfds); 


    if (!fork()) { 
     printf("Child: Waiting to acquire semaphore\n"); 
     sem_wait(&sem); 
     printf("child acquires lock\n"); 
     sleep(5); 
     write(pfds[1], "Hello", 6); /* make stdout same as pfds[1] */ 
     close(pfds[0]); /* we don't need this */ 
     printf("child releases lock\n"); 
     sem_post(&sem); 
    } 
    else { 
     printf("Parent: Waiting to acquire semaphore\n"); 
     sem_wait(&sem); 
     printf("Parent acquires lock\n"); 
     read(pfds[0],buff,6); /* we don't need this */ 
     printf("Parent read: %s",buff); 
     printf("parent releases lock\n"); 
     sem_post(&sem); 
    } 
    sem_destroy(&sem); 
    return 0; 
} 

Выше - это простая трубка, которую я создал, в которой ребенок пишет, а родительский читает. Я поставил переменную семафора, инициализированную на 1, так что, пока ребенок пишет, родитель должен ждать. Добавлен умышленный «сон», чтобы увидеть, что печатается родительский процесс, вращающийся «Родитель: Ожидание получения семафора».POSIX Семафор: Почему родительский процесс получает семафор до того, как его освободит?

The expected sequence should be: 
Child: Waiting to acquire semaphore 
child acquires lock...(delay of 5 secs here) 
child releases lock 

Parent: Waiting to acquire semaphore 
Parent acquires lock 
Parent read..bla-bla 
parent releases lock 

However it happens: 
[[email protected] interview]# ./a.out 
Child: Waiting to acquire semaphore 
child acquires lock 
Parent: Waiting to acquire semaphore 
Parent acquires lock -> NOTE: Parent acquires lock before child releases it 
child releases lock 
Parent read: Hello 
parent releases lock 

Вопрос: Каким образом родитель получает блокировку в то время как ребенок задерживается и до сих пор держит его, и еще не выпустили семафора?

Кроме того, есть ли способ убедиться, что ребенок всегда сначала приобретает семафор, так как он должен писать в трубу?

EDIT: Благодаря @jxh. Вот модифицированный код, который отлично работает (вставка для ссылки каждого).

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <semaphore.h> 
#include <sys/mman.h> 

int main(void) 
{ 
    int pfds[2]; 
    char buff[20]; 


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


    if (!fork()) { 
     write(pfds[1], "Hello", 6); /* make stdout same as pfds[1] */ 
     close(pfds[0]); /* we don't need this */ 
     printf("child releases semaphore\n"); 
     sem_post(sem); 
    } 
    else { 
     printf("Parent: Waiting to acquire semaphore\n"); 
     sem_wait(sem); 
     printf("Parent acquires lock\n"); 
     read(pfds[0],buff,6); /* we don't need this */ 
     printf("Parent read: %s\n",buff); 
     printf("parent releases lock\n"); 
     sem_post(sem); 
    } 
    sem_destroy(sem); 
    return 0; 
} 

ответ

3

После fork() нет никакой гарантии, что ребенок проходит перед родителем, и я уверен, что это, как правило, родительский процесс, который продолжает выполнение на планировщик ОС после fork().

семафора должно быть в общей памяти, как это было предложено VoidPointer и QWR, и указывается в man page:

Если pshared отличен от нуля, то семафор разделяется между процессами, и должны расположены в области разделяемой памяти (см shm_open(3), mmap(2) и shmget(2))

можно выделить семафор в разделяемой памяти с mmap(), как это:.

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

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

Вместо того, чтобы звонить как sem_wait(), вы можете инициализировать семафор до 0 и разрешить родительскому вызову sem_wait(). Ребенок не ждет, но звонит sem_post() после того, как это будет сделано, что просыпает родителя.

+0

Но как же родитель приобретает блокировку до того, как ребенок выпустит ее? Странно – kingsmasher1

+0

@ kingsmasher1: Ваш родитель и ребенок участвуют в гонке за приобретение семафора одновременно. Это не то, что вы хотите. Инициализируйте семафор на '0' вместо этого, чтобы родительский блок заблокировал ожидание для дочернего элемента. Удалите вызов из 'sem_wait()' из дочернего элемента. – jxh

+0

Я расскажу интересную вещь. Вы знаете, тот же код, просто инициализирующий семафор до «0», приводит к взаимоблокировке. И родительский, и детский отпечатки: «Ожидание приобретения ..» и продолжает ждать :-) – kingsmasher1

1

http://linux.die.net/man/7/sem_overview Процесс совместно используемый семафор должен быть помещен в области совместно используемой памяти (например, System V сегмент общей памяти создан при shmget (2), или POSIX, общего объект встроенной памяти создан при shm_open (3)).

+0

@torek: Вы уверены. На странице руководства кажется, что семафор уже должен находиться в общей памяти, а плакат сообщает о поведении, как я ожидал бы, если семафор не был разделен (в комментариях к моему ответу). – jxh

+0

@torek hi torek .i думаю, его проблема из-за общей памяти. его логика кажется правильной. – qwr

+0

Я был в курсе внедрения и думал, что он автоматически делится, затем я тестировал и ... это не так. Поэтому я ошибся. – torek

2

После fork родитель и ребенок будут содержать переменную sem в совершенно другой области. Выполнение sem_wait(&sem) будет ссылаться на совершенно другую память в родительском и дочернем, и, следовательно, оба приобретают семафор.

Если вы хотите семафор между различными процессами, то вы должны использовать shared memory, как указано на странице руководства пользователя sem_init here, а также страницу открывания глаз is here.

+0

+1 за то, что заставило меня внимательно прочитать страницу руководства. – jxh

+0

@jxh (а также at-me!): Страница [POSIX] (http://pubs.opengroup.org/onlinepubs/009695399/functions/sem_init.html) выглядит довольно неоднозначной, ее можно интерпретировать как «пользователь должен link sem в общую область "или" система будет выделять общую область и сделать что-то в sem point к этому ". Похоже, что разные страницы руководства менее двусмысленны. – torek

+0

@torek: Да, изначально у меня была такая же реакция кишки, но то, что изменило мой разум, было «должно» быть в общей памяти. Я решил, что в руководстве будет указано, как семафор будет изменен, он сказал бы «будет» в общей памяти. – jxh