2013-06-13 2 views
2

Я написал программу C для проблемы столовых философов, используя pthread некоторое время назад, и теперь я пытаюсь изменить ее для использования fork(). Это упражнение для лекции, которую я уже прошел. Но друг попросил меня о помощи, и я не могу понять, что это выяснилось мною, что сводит меня с ума!Рестораны Философы в C с использованием fork()

Если я выполняю «ps», процессы там есть. Но нет вывода на stdout, поэтому я думаю, что я делаю что-то неправильно с трубами.

#include <stdio.h> 
#include <stdlib.h> 
#include <semaphore.h> 
#include <pthread.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/wait.h> 

#define N 5  
#define LEFT (i+4)%N 
#define RIGHT (i+1)%N 
#define THINKING 0 
#define HUNGRY 1 
#define EATING 2 

sem_t spoon; 
sem_t phil[N]; 
int state[N]; 
int phil_num[N]={0,1,2,3,4}; 
int fd[N][2]; // file descriptors for pipes 
pid_t pid, pids[N]; // process ids 
int i; 
int num; 

void philosopher(int i); 
void test(int i); 
void take_spoon(int i); 
void put_spoon(int i); 

char buffer[100]; 

int main(void) 
{ 
    for(i=0;i<N;++i) 
    { 
    pipe(fd[i]);   
    pids[i] = fork(); 

    printf("i=%d\n",i); 
    printf("pids[i]=%d\n",pids[i]); 

    if(pids[i]==0) 
    { 
     // child 
     dup2(fd[i][1],1); 
     close(fd[i][0]);  
     close(fd[i][1]); 
     philosopher(i); 
     _exit(0); 
    } 

    else if(pids[i]>0) 
    { 
     // parent 
     dup2(fd[i][0],0); 
     close(fd[i][0]);  
     close(fd[i][1]); 
    } 
    } 

    // wait for child processes to end 
    for(i=0;i<N;++i) waitpid(pids[i],NULL,0); 

    return 0; 
} 



void philosopher(int i) 
{ 
    while(1) 
    { 
    sleep(1); 
    take_spoon(i); 
    sleep(2); 
    put_spoon(i); 
    sleep(1); 
    } 
} 

void take_spoon(int i) 
{ 
    sem_wait(&spoon); 
    state[i] = HUNGRY; 
    printf("philosopher %d is hungry\n",i+1); 
    test(i); 
    sem_post(&spoon); 
    sem_wait(&phil[i]); 
} 

void put_spoon(int i) 
{ 
    sem_wait(&spoon); 
    state[i] = THINKING; 
    printf("philosopher %d puts down spoon %d and %d hin\n",i+1,LEFT+1,i+1); 
    printf("philosopher %d thinks\n",i+1); 
    test(LEFT); 
    test(RIGHT); 
    sem_post(&spoon); 
} 

void test(int i) 
{ 
    if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) 
    { 
    state[i] = EATING; 
    printf("philosopher %d takes spoon %d and %d\n",i+1,LEFT+1,i+1); 
    printf("philosopher %d eats\n",i+1); 
    sem_post(&phil[i]); 
    } 
} 

Заранее благодарю за любую помощь.

ответ

2

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

Во-вторых, у вас есть некоторые путаные ненужные переменные. В частности, трубы не служат никакой реальной цели. stdout для каждого из процессов перейдет на консольный экран уже без необходимости попытки вернуть их обратно родительскому. Это связано с тем, что дочерний процесс уже наследует дескрипторы открытого файла родителя, поэтому ребенок уже будет использовать тот же stdout, что и родительский. Кроме того, переменные phil_num и num не использовались, а i. pid и pids переменные, казалось, были бесполезно сделаны глобальными.

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

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

struct shared_data { 
    sem_t spoon; 
    sem_t phil[N]; 
    int state[N]; 
}; 

struct shared_data *shared; 

void initialize_shared(); /* at program start */ 
void finalize_shared(); /* at program end */ 

Один из способов создания совместно используемой памяти является использование mmap(). После создания памяти данные должны быть инициализированы правильно. Это включает вызов sem_init() на семафорах. sem_destroy() используется для очистки семафора, а отображаемая память может быть освобождена с помощью munmap(). Это делается для вас, когда процесс завершается, но обеспечивается полнотой. (Вы должны всегда проверять возвращаемые значения всех операционной системы вызовов вы делаете, но я опущены их для краткости.)

void initialize_shared() 
{ 
    int i; 
    int prot=(PROT_READ|PROT_WRITE); 
    int flags=(MAP_SHARED|MAP_ANONYMOUS); 
    shared=mmap(0,sizeof(*shared),prot,flags,-1,0); 
    memset(shared,'\0',sizeof(*shared)); 
    sem_init(&shared->spoon,1,1); 
    for(i=0;i<N;++i) sem_init(&shared->phil[i],1,1); 
} 

void finalize_shared() 
{ 
    int i; 
    for(i=0;i<N;++i) sem_destroy(&shared->phil[i]); 
    munmap(shared, sizeof(*shared)); 
} 

Ваш main() реализация не меняет, кроме вас, необходимо добавить локальный переменные для тех, которые были бесполезно глобальными, а также вызывали initialize_shared() и необязательно finalize_shared(). Также удалите весь код, связанный с pipe().

int main(void) 
{ 
    int i; 
    pid_t pid, pids[N]; // process ids 
    initialize_shared(); 
    for(i=0;i<N;++i) 
    { 
    pid = fork(); 
    if(pid==0) 
    { 
     // child 
     philosopher(i); 
     _exit(0); 
    } 
    else if(pid>0) 
    { 
     // parent 
     pids[i] = pid; 
     printf("pids[%d]=%d\n",i,pids[i]); 
    } 
    else 
    { 
     perror("fork"); 
     _exit(0); 
    } 
    } 
    // wait for child processes to end 
    for(i=0;i<N;++i) waitpid(pids[i],NULL,0); 

    finalize_shared(); 
    return 0; 
} 

Обратите внимание, что ваша программа никогда действительно не выходит самостоятельно, так как philosopher() реализован в виде бесконечного цикла.

+0

Большое спасибо за этот отличный андерсер. Это очень информативно. Те неиспользуемые переменные, о которых вы упомянули, остались в стороне от других вещей, которые я пытался, которых я забыл удалить, извините за это. – sfey

+1

Нет проблем. Я надеюсь, что это помогает. Вы по-прежнему новичок в StackOverflow, поэтому я бы рекомендовал вам прочитать [about page] (http://stackoverflow.com/about). – jxh

+0

Да, ваш ответ действительно был очень полезным, спасибо. Хоть все-таки внести небольшие коррективы здесь и там, но теперь он работает. – sfey

Смежные вопросы